From db13eea2fcbb6c3022e0728d4580d4585a204a00 Mon Sep 17 00:00:00 2001 From: Bobby Dilley Date: Thu, 28 Jul 2022 12:43:10 +0100 Subject: [PATCH] Add sound stuff --- .vscode/settings.json | 11 + Makefile | 9 +- src/libsegaapi/Makefile | 11 + src/libsegaapi/segaapi.c | 1148 ++++++++++++++++++++++++++ src/libsegaapi/segaapi.h | 1647 ++++++++++++++++++++++++++++++++++++++ src/libsegaapi/segadef.h | 78 ++ src/libsegaapi/segaeax.h | 1478 ++++++++++++++++++++++++++++++++++ src/libsegaapi/segaerr.h | 43 + src/libsegaapi/tsf.h | 1640 +++++++++++++++++++++++++++++++++++++ 9 files changed, 6064 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json create mode 100644 src/libsegaapi/Makefile create mode 100644 src/libsegaapi/segaapi.c create mode 100644 src/libsegaapi/segaapi.h create mode 100644 src/libsegaapi/segadef.h create mode 100644 src/libsegaapi/segaeax.h create mode 100644 src/libsegaapi/segaerr.h create mode 100644 src/libsegaapi/tsf.h diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7948bf6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.associations": { + "string.h": "c", + "rideboard.h": "c", + "serial.h": "c", + "jvs.h": "c", + "time.h": "c", + "stdarg.h": "c", + "ioctl.h": "c" + } +} diff --git a/Makefile b/Makefile index 0e294ad..7adc604 100644 --- a/Makefile +++ b/Makefile @@ -7,12 +7,19 @@ BUILD = build OBJS := $(patsubst %.c,%.o,$(wildcard src/lindbergh/*.c)) -all: lindbergh.so +all: lindbergh.so libsegaapi.so lindbergh.so: $(OBJS) mkdir -p $(BUILD) $(LD) $(OBJS) $(LDFLAGS) $(CFLAGS) -o $(BUILD)/lindbergh.so rm -f src/lindbergh/*.o +LIBSEGA_LD=gcc #clang +LIBSEGA_LDFLAGS=-m32 -O0 -g + +libsegaapi.so: src/libsegaapi/segaapi.o + $(LIBSEGA_LD) $(LIBSEGA_LDFLAGS) src/libsegaapi/segaapi.o -L/usr/lib/i386-linux-gnu -lalut -fPIC -shared -o $(BUILD)/libsegaapi.so + rm -f src/libsegaapi/*.o + clean: rm -rf $(BUILD) diff --git a/src/libsegaapi/Makefile b/src/libsegaapi/Makefile new file mode 100644 index 0000000..dea6341 --- /dev/null +++ b/src/libsegaapi/Makefile @@ -0,0 +1,11 @@ +LD=gcc #clang +CC=gcc +CFLAGS=-m32 -O0 -g +LDFLAGS=$(CFLAGS) + +libsegaapi.so: segaapi.o + $(LD) $(LDFLAGS) segaapi.o -L/usr/lib/i386-linux-gnu -lalut -fPIC -shared -o libsegaapi.so +clean: + rm -f test libsegaapi.so + +force: diff --git a/src/libsegaapi/segaapi.c b/src/libsegaapi/segaapi.c new file mode 100644 index 0000000..5b35cd6 --- /dev/null +++ b/src/libsegaapi/segaapi.c @@ -0,0 +1,1148 @@ +/* + 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 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "eax4.h" +#include "segadef.h" +#include "segaerr.h" +#include "segaeax.h" + +#include "segaapi.h" + +#define TSF_IMPLEMENTATION +#include "tsf.h" + +// help and info +// 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 + +//#define DEBUG_SAMPLE +#define DEBUG_OUTPUT +//#define DUMP_WAV +//#define DUMP_BUFFER + +#define CHECK() \ + { \ + printf("%s %d\n", __func__, __LINE__); \ + unsigned int err; \ + if ((err = alGetError()) != AL_NO_ERROR) \ + { \ + printf("%s %d\n", __func__, __LINE__); \ + dbgPrint(":%i AL Error: 0x%08X\n", __LINE__, err); \ + exit(2); \ + } \ + } + +SEGASTATUS g_LastStatus = SEGA_SUCCESS; + +// 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 +{ + 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; + bool playWithSetup; + + ALuint alBuffer; + ALuint alSource; + + tsf *synth; + struct tsf_region *region; +} segaapiContext_t; + +LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = NULL; +LPALBUFFERSUBSAMPLESSOFT alBufferSubSamplesSOFT = NULL; +LPALGETBUFFERSAMPLESSOFT alGetBufferSamplesSOFT = NULL; + +#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 + +#ifdef DUMP_BUFFER +static void dumpBuffer(const char *path, void *data, size_t size) +{ + FILE *soundFile = NULL; + + soundFile = fopen(path, "wb"); + fwrite(data, size, 1, soundFile); + fclose(soundFile); +} +#endif + +#ifdef DUMP_WAV +static void dumpWaveBuffer(const char *path, unsigned int channels, unsigned int sampleRate, unsigned int sampleBits, void *data, size_t size) +{ + + struct RIFF_Header + { + char chunkID[4]; + long chunkSize; // size not including chunkSize or chunkID + char format[4]; + }; + + struct WAVE_Format + { + char subChunkID[4]; + long subChunkSize; + short audioFormat; + short numChannels; + long sampleRate; + long byteRate; + short blockAlign; + short bitsPerSample; + }; + + struct WAVE_Data + { + char subChunkID[4]; // should contain the word data + long subChunk2Size; // Stores the size of the data block + }; + + // Local Declarations + FILE *soundFile = NULL; + struct WAVE_Format wave_format; + struct RIFF_Header riff_header; + struct WAVE_Data wave_data; + + soundFile = fopen(path, "wb"); + + // check for RIFF and WAVE tag in memeory + riff_header.chunkID[0] = 'R'; + riff_header.chunkID[1] = 'I'; + riff_header.chunkID[2] = 'F'; + riff_header.chunkID[3] = 'F'; + riff_header.format[0] = 'W'; + riff_header.format[1] = 'A'; + riff_header.format[2] = 'V'; + riff_header.format[3] = 'E'; + + // Read in the first chunk into the struct + fwrite(&riff_header, sizeof(struct RIFF_Header), 1, soundFile); + + // check for fmt tag in memory + wave_format.subChunkID[0] = 'f'; + wave_format.subChunkID[1] = 'm'; + wave_format.subChunkID[2] = 't'; + wave_format.subChunkID[3] = ' '; + + wave_format.audioFormat = 1; + wave_format.sampleRate = sampleRate; + wave_format.numChannels = channels; + wave_format.bitsPerSample = sampleBits; + wave_format.byteRate = (sampleRate * sampleBits * channels) / 8; + wave_format.blockAlign = (sampleBits * channels) / 8; + wave_format.subChunkSize = 16; + + // Read in the 2nd chunk for the wave info + fwrite(&wave_format, sizeof(struct WAVE_Format), 1, soundFile); + + // Read in the the last byte of data before the sound file + + // check for data tag in memory + wave_data.subChunkID[0] = 'd'; + wave_data.subChunkID[1] = 'a'; + wave_data.subChunkID[2] = 't'; + wave_data.subChunkID[3] = 'a'; + + wave_data.subChunk2Size = size; + + fwrite(&wave_data, sizeof(struct WAVE_Data), 1, soundFile); + + // Read in the sound data into the soundData variable + fwrite(data, wave_data.subChunk2Size, 1, soundFile); + + // clean up and return true if successful + fclose(soundFile); + + return; +} +#endif + + +ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type) +{ + 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; + } + + 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; +} + + +static unsigned int bufferSampleSize(segaapiContext_t *context) +{ // printf("%s %d\n", __func__, __LINE__); + return context->channels * ((context->sampleFormat == HASF_SIGNED_16PCM) ? 2 : 1); +} + +static void updateBufferLoop(segaapiContext_t *context) +{ // printf("%s %d\n", __func__, __LINE__); + if (context == NULL) + return; + unsigned int sampleSize = bufferSampleSize(context); + alSourcei(context->alSource, AL_BUFFER, AL_NONE); + // CHECK(); + /* + 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(); + */ + return; +} + +void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate, + ALenum internalformat, ALsizei samples, + ALenum channels, ALenum type, + const ALvoid *data) +{ + alBufferData(buffer, internalformat, data, + FramesToBytes(samples, channels, type), + samplerate); +} + + +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; + } + if (alFormat == -1) + { + dbgPrint("Unknown format! 0x%X with %u channels!\n", context->sampleFormat, context->channels); + } + + ALint position; + alGetSourcei(context->alSource, AL_SAMPLE_OFFSET, &position); // TODO: Patch if looping is active + // CHECK(); + + ALint unsafe[2]; + alGetSourceiv(context->alSource, AL_BYTE_RW_OFFSETS_SOFT, unsafe); // AL_BYTE_OFFSET + // CHECK(); + if (offset != -1) + { + //printf("CANNOT DO IT\n"); + //exit(1); + wrap_BufferSamples(context->alBuffer, context->sampleRate, alFormat, context->size / bufferSampleSize(context), alChannels, alType, &(context->data[offset])); + + //alBufferSubSamplesSOFT(context->alBuffer, offset / bufferSampleSize(context), length / bufferSampleSize(context), alChannels, alType, &context->data[offset]); + // CHECK(); + 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]); + } + else + { + + + alSourcei(context->alSource, AL_BUFFER, AL_NONE); + // CHECK(); + wrap_BufferSamples(context->alBuffer, context->sampleRate, alFormat, context->size / bufferSampleSize(context), alChannels, alType, context->data); + // CHECK(); + alSourcei(context->alSource, AL_BUFFER, context->alBuffer); + // CHECK(); + 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]); + } + +#ifdef DUMP_WAV + // This dumps the buffer + if (context->data != NULL) + { + uint8_t *nonZero = context->data; + while (*nonZero++ == 0x00) + ; + if ((nonZero - (uint8_t *)context->data) < context->size) + { + printf("%s %d\n", __func__, __LINE__); + char buf[1000]; + sprintf(buf, "SAMPLE-%X-%i-%04X-%u.wav", (uintptr_t)context, context->channels, context->sampleFormat, context->sampleRate); + dbgPrint("Writing: %s (%i)", buf, context->size); + void *tmp = malloc(context->size); + if (tmp != NULL) + { + alGetBufferSamplesSOFT(context->alBuffer, 0, context->size / bufferSampleSize(context), alChannels, alType, tmp); + // CHECK(); + dumpWaveBuffer(buf, context->channels, context->sampleRate, (context->sampleFormat == HASF_SIGNED_16PCM) ? 16 : 8, tmp, context->size); + free(tmp); + } + } + } +#endif + return; +} + +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; + context->playWithSetup = 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); +} + +SEGASTATUS SEGAAPI_Play(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_Play() 0x%x", hHandle); + segaapiContext_t *context = hHandle; + if (context == NULL) + return SEGAERR_BAD_PARAM; +#ifdef DUMP_BUFFER + if (context->data != NULL) + { + if (context->synthesizer) + { + if (context->data) + { + char filename[1000]; + sprintf(filename, "SYNTH-%X-%i-%04X-%u.bin", (uintptr_t)context, context->channels, context->sampleFormat, context->sampleRate); + dbgPrint("Writing: %s (%i)", filename, context->size); + dumpBuffer(filename, context->data, context->size); + } + } + } +#endif + + alSourcei(context->alSource, AL_LOOPING, context->loop ? AL_TRUE : AL_FALSE); + alSourcei(context->alSource, AL_BUFFER, context->alBuffer); + alSourcePlay(context->alSource); + return SEGA_SUCCESS; +} + +SEGASTATUS SEGAAPI_Pause(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_Pause() 0x%x", hHandle); + segaapiContext_t *context = hHandle; + if (context == NULL) + return SEGAERR_BAD_PARAM; + alSourcePause(context->alSource); + return SEGA_SUCCESS; +} + +SEGASTATUS SEGAAPI_Stop(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_Stop() 0x%x", hHandle); + segaapiContext_t *context = hHandle; + if (context == NULL) + return SEGAERR_BAD_PARAM; + alSourceStop(context->alSource); + return SEGA_SUCCESS; +} + +SEGASTATUS SEGAAPI_PlayWithSetup(CTHANDLE hHandle) +{ + 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; +} + +HAWOSTATUS SEGAAPI_GetPlaybackStatus(CTHANDLE hHandle) +{ + ALint state; + + dbgPrint("SEGAAPI_GetPlaybackStatus() 0x%x", hHandle); + segaapiContext_t *context = hHandle; + if (context == NULL) + return HAWOSTATUS_INVALID; + + alGetSourcei(context->alSource, AL_SOURCE_STATE, &state); + switch (state) + { + case AL_PLAYING: + return HAWOSTATUS_ACTIVE; + case AL_PAUSED: + return HAWOSTATUS_PAUSE; + case AL_INITIAL: + case AL_STOPPED: + return HAWOSTATUS_STOP; + default: + return HAWOSTATUS_INVALID; + } + + return HAWOSTATUS_INVALID; +} + +SEGASTATUS SEGAAPI_SetFormat(CTHANDLE hHandle, HAWOSEFORMAT *pFormat) +{ + dbgPrint("SEGAAPI_SetFormat() 0x%x", hHandle); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_GetFormat(CTHANDLE hHandle, HAWOSEFORMAT *pFormat) +{ + dbgPrint("SEGAAPI_GetFormat() 0x%x", hHandle); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_SetSampleRate(CTHANDLE hHandle, CTDWORD dwSampleRate) +{ + dbgPrint("SEGAAPI_SetSampleRate() 0x%x 0x%x", hHandle, dwSampleRate); + 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->sampleRate = dwSampleRate; + updateBufferData(context, -1, -1); + return SEGA_SUCCESS; +} + +CTDWORD SEGAAPI_GetSampleRate(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_GetSampleRate() 0x%x", hHandle); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_SetPriority(CTHANDLE hHandle, CTDWORD dwPriority) +{ + dbgPrint("SEGAAPI_SetPriority() 0x%x 0x%x", hHandle, dwPriority); + return SEGAERR_UNSUPPORTED; +} + +CTDWORD SEGAAPI_GetPriority(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_GetPriority() 0x%x", hHandle); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_SetUserData(CTHANDLE hHandle, CTHANDLE hUserData) +{ + 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; +} + +CTHANDLE SEGAAPI_GetUserData(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_GetPriority() 0x%x", hHandle); + if (hHandle == NULL) + return NULL; + segaapiContext_t *context = hHandle; + return context->userData; +} + +SEGASTATUS SEGAAPI_SetSendRouting(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend, HAROUTING dwDest) +{ + dbgPrint("SEGAAPI_SetSendRouting() 0x%x 0x%x 0x%x 0x%x", hHandle, dwChannel, dwSend, dwDest); + return SEGA_SUCCESS; +} + +HAROUTING SEGAAPI_GetSendRouting(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend) +{ + dbgPrint("SEGAAPI_GetSendRouting() 0x%x 0x%x 0x%x", hHandle, dwChannel, dwSend); + return HA_UNUSED_PORT; +} + +SEGASTATUS SEGAAPI_SetSendLevel(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend, CTDWORD dwLevel) +{ + dbgPrint("SEGAAPI_SetSendLevel() 0x%x 0x%x 0x%x 0x%x", hHandle, dwChannel, dwSend, dwLevel); + return SEGA_SUCCESS; +} + +CTDWORD SEGAAPI_GetSendLevel(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend) +{ + dbgPrint("SEGAAPI_GetSendLevel() 0x%x 0x%x 0x%x", hHandle, dwChannel, dwSend); + return 0; +} + +SEGASTATUS SEGAAPI_SetChannelVolume(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwVolume) +{ + dbgPrint("SEGAAPI_SetChannelVolume() 0x%x 0x%x 0x%x", hHandle, dwChannel, dwVolume); + return SEGAERR_UNSUPPORTED; +} + +CTDWORD SEGAAPI_GetChannelVolume(CTHANDLE hHandle, CTDWORD dwChannel) +{ + dbgPrint("SEGAAPI_GetChannelVolume() 0x%x 0x%x", hHandle, dwChannel); + return 0; +} + +SEGASTATUS SEGAAPI_SetPlaybackPosition(CTHANDLE hHandle, CTDWORD dwPlaybackPos) +{ + dbgPrint("SEGAAPI_SetPlaybackPosition() 0x%x 0x%x", hHandle, dwPlaybackPos); + segaapiContext_t *context = hHandle; + alSourcei(context->alSource, AL_BYTE_OFFSET, dwPlaybackPos); + return SEGA_SUCCESS; +} + +CTDWORD SEGAAPI_GetPlaybackPosition(CTHANDLE hHandle) +{ + ALint position; + dbgPrint("SEGAAPI_GetPlaybackPosition() 0x%x", hHandle); + segaapiContext_t *context = hHandle; + alGetSourcei(context->alSource, AL_BYTE_OFFSET, &position); + return position; +} + +SEGASTATUS SEGAAPI_SetNotificationFrequency(CTHANDLE hHandle, CTDWORD dwFrameCount) +{ + dbgPrint("SEGAAPI_SetNotificationFrequency() 0x%x 0x%x", hHandle, dwFrameCount); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_SetNotificationPoint(CTHANDLE hHandle, CTDWORD dwBufferOffset) +{ + dbgPrint("SEGAAPI_SetNotificationPoint() 0x%x 0x%x", hHandle, dwBufferOffset); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_ClearNotificationPoint(CTHANDLE hHandle, CTDWORD dwBufferOffset) +{ + dbgPrint("SEGAAPI_ClearNotificationPoint() 0x%x 0x%x", hHandle, dwBufferOffset); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_SetStartLoopOffset(CTHANDLE hHandle, CTDWORD dwOffset) +{ + 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; +} + +CTDWORD SEGAAPI_GetStartLoopOffset(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_GetStartLoopOffset() 0x%x", hHandle); + return 0; +} + +SEGASTATUS SEGAAPI_SetEndLoopOffset(CTHANDLE hHandle, CTDWORD dwOffset) +{ + 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; +} + +CTDWORD SEGAAPI_GetEndLoopOffset(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_GetEndLoopOffset() 0x%x", hHandle); + return 0; +} + +SEGASTATUS SEGAAPI_SetEndOffset(CTHANDLE hHandle, CTDWORD dwOffset) +{ + dbgPrint("SEGAAPI_SetEndOffset() 0x%x 0x%x", hHandle, dwOffset); + return SEGAERR_UNSUPPORTED; +} + +CTDWORD SEGAAPI_GetEndOffset(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_GetEndOffset() 0x%x", hHandle); + return 0; +} + +SEGASTATUS SEGAAPI_SetLoopState(CTHANDLE hHandle, CTBOOL bDoContinuousLooping) +{ + 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; +} + +CTBOOL SEGAAPI_GetLoopState(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_GetLoopState() 0x%x", hHandle); + return 0; +} + +SEGASTATUS SEGAAPI_UpdateBuffer(CTHANDLE hHandle, CTDWORD dwStartOffset, CTDWORD dwLength) +{ + 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; +} + +SEGASTATUS SEGAAPI_SetSynthParam(CTHANDLE hHandle, HASYNTHPARAMSEXT param, CTLONG lPARWValue) +{ + float volume; + float semiTones; + float freqRatio; + + dbgPrint("SEGAAPI_SetSynthParam() 0x%x 0x%x 0x%x", hHandle, param, lPARWValue); + + segaapiContext_t *context = hHandle; + 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; +} + +CTLONG SEGAAPI_GetSynthParam(CTHANDLE hHandle, HASYNTHPARAMSEXT param) +{ + dbgPrint("SEGAAPI_GetSynthParam() 0x%x 0x%x", hHandle, param); + return 0; +} + +SEGASTATUS SEGAAPI_SetSynthParamMultiple(CTHANDLE hHandle, CTDWORD dwNumParams, SynthParamSet *pSynthParams) +{ + 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; +} + +SEGASTATUS SEGAAPI_GetSynthParamMultiple(CTHANDLE hHandle, CTDWORD dwNumParams, SynthParamSet *pSynthParams) +{ + dbgPrint("SEGAAPI_GetSynthParamMultiple() 0x%x 0x%x 0x%x", hHandle, dwNumParams, pSynthParams); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_SetReleaseState(CTHANDLE hHandle, CTBOOL bSet) +{ + dbgPrint("SEGAAPI_SetReleaseState() 0x%x 0x%x", hHandle, bSet); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_CreateBuffer(HAWOSEBUFFERCONFIG *pConfig, HAWOSEGABUFFERCALLBACK pCallback, CTDWORD dwFlags, CTHANDLE *phHandle) +{ + dbgPrint("SEGAAPI_CreateBuffer() 0x%x 0x%x 0x%x 0x%x", pConfig, pCallback, dwFlags, phHandle); + if ((phHandle == NULL) || (pConfig == NULL)) + { + g_LastStatus = SEGAERR_BAD_POINTER; + dbgPrint("SEGAAPI_CreateBuffer() SEGAERR_BAD_POINTER"); + return SEGAERR_BAD_POINTER; + } + segaapiContext_t *context = malloc(sizeof(segaapiContext_t)); + if (context == NULL) + { + dbgPrint("SEGAAPI_CreateBuffer() SEGAERR_OUT_OF_MEMORY"); + return SEGAERR_OUT_OF_MEMORY; + } + // 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; +} + +SEGASTATUS SEGAAPI_DestroyBuffer(CTHANDLE hHandle) +{ + dbgPrint("SEGAAPI_DestroyBuffer() 0x%x", hHandle); + if (hHandle == NULL) + return SEGAERR_BAD_PARAM; + free(hHandle); + return SEGA_SUCCESS; +} + +CTBOOL SEGAAPI_SetGlobalEAXProperty(GUID *guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize) +{ + dbgPrint("SEGAAPI_SetGlobalEAXProperty() 0x%x 0x%x 0x%x 0x%x", guid, ulProperty, pData, ulDataSize); + return 0; +} + +CTBOOL SEGAAPI_GetGlobalEAXProperty(GUID *guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize) +{ + dbgPrint("SEGAAPI_GetGlobalEAXProperty() 0x%x 0x%x 0x%x 0x%x", guid, ulProperty, pData, ulDataSize); + return 0; +} + +SEGASTATUS SEGAAPI_SetSPDIFOutChannelStatus(CTDWORD dwChannelStatus, CTDWORD dwExtChannelStatus) +{ + dbgPrint("SEGAAPI_SetSPDIFOutChannelStatus() 0x%x 0x%x", dwChannelStatus, dwExtChannelStatus); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_GetSPDIFOutChannelStatus(CTDWORD *pdwChannelStatus, CTDWORD *pdwExtChannelStatus) +{ + dbgPrint("SEGAAPI_GetSPDIFOutChannelStatus() 0x%x 0x%x", pdwChannelStatus, pdwExtChannelStatus); + return SEGAERR_UNSUPPORTED; +} + +SEGASTATUS SEGAAPI_SetSPDIFOutSampleRate(HASPDIFOUTRATE dwSamplingRate) +{ + dbgPrint("SEGAAPI_SetSPDIFOutSampleRate() 0x%x", dwSamplingRate); + return SEGAERR_UNSUPPORTED; +} + +HASPDIFOUTRATE SEGAAPI_GetSPDIFOutSampleRate(void) +{ + dbgPrint("SEGAAPI_GetSPDIFOutSampleRate()"); + return HASPDIFOUT_48KHZ; +} + +SEGASTATUS SEGAAPI_SetSPDIFOutChannelRouting(CTDWORD dwChannel, HAROUTING dwSource) +{ + 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; +} + +HAROUTING SEGAAPI_GetSPDIFOutChannelRouting(CTDWORD dwChannel) +{ + dbgPrint("SEGAAPI_GetSPDIFOutChannelRouting() 0x%x", dwChannel); + return HA_UNUSED_PORT; +} + +SEGASTATUS SEGAAPI_SetIOVolume(HAPHYSICALIO dwPhysIO, CTDWORD dwVolume) +{ + // float v = (dwVolume >> 16) & 0xffff; + dbgPrint("SEGAAPI_SetIOVolume() 0x%x 0x%x", dwPhysIO, dwVolume); + // alListenerf(AL_GAIN, v); + return SEGA_SUCCESS; +} + +CTDWORD SEGAAPI_GetIOVolume(HAPHYSICALIO dwPhysIO) +{ + dbgPrint("SEGAAPI_GetIOVolume() 0x%x", dwPhysIO); + return 0xffffffff; +} + +void SEGAAPI_SetLastStatus(SEGASTATUS LastStatus) +{ + dbgPrint("SEGAAPI_SetLastStatus() 0x%x", LastStatus); + return; +} + +SEGASTATUS SEGAAPI_GetLastStatus(void) +{ + dbgPrint("SEGAAPI_GetLastStatus()"); + return SEGA_SUCCESS; +} + +SEGASTATUS SEGAAPI_Reset(void) +{ + dbgPrint("SEGAAPI_Reset()"); + return SEGA_SUCCESS; +} + +SEGASTATUS SEGAAPI_Init(void) +{ + dbgPrint("SEGAAPI_Init()"); + + int res = alutInit(NULL, NULL); + if (res == AL_FALSE) + { + dbgPrint("SEGAAPI_Init() alutInit failed"); + return SEGAERR_FAIL; + } + + alBufferSamplesSOFT = alGetProcAddress("alBufferSamplesSOFT"); + if (alBufferSamplesSOFT == NULL) + { + dbgPrint("Could not resolve AL extension!\n"); + //exit(1); + } + + alBufferSubSamplesSOFT = alGetProcAddress("alBufferSubSamplesSOFT"); + if (alBufferSubSamplesSOFT == NULL) + { + dbgPrint("Could not resolve AL extension!\n"); + //exit(1); + } + alGetBufferSamplesSOFT = alGetProcAddress("alGetBufferSamplesSOFT"); + if (alGetBufferSamplesSOFT == NULL) + { + dbgPrint("Could not resolve AL extension!\n"); + //exit(1); + } + + SEGAAPI_SetGlobalEAXProperty((GUID *)&EAXPROPERTYID_EAX40_FXSlot2, 0, (void *)&EAX_NULL_GUID, 16); + + SEGAAPI_SetSPDIFOutChannelRouting(0, 0); + SEGAAPI_SetSPDIFOutChannelRouting(1, 1); + SEGAAPI_SetSPDIFOutSampleRate(1); + return SEGA_SUCCESS; +} + +SEGASTATUS SEGAAPI_Exit(void) +{ + dbgPrint("SEGAAPI_Exit()"); + alutExit(); + return SEGA_SUCCESS; +} diff --git a/src/libsegaapi/segaapi.h b/src/libsegaapi/segaapi.h new file mode 100644 index 0000000..c333bd5 --- /dev/null +++ b/src/libsegaapi/segaapi.h @@ -0,0 +1,1647 @@ +/** + * Copyright (C) 2004-2005 Creative Technology Ltd. All rights reserved. + * + **************************************************************************** + * \file segaapi.h + * \brief + * This file contains the definition of the interfaces that requested by SEGA + * for audio output support. + * + * + * @author Creative + * + * $Date: 2006/01/03 06:54:39 $ + * + **************************************************************************** + * Revision History: + * + * 0.5 + * 1st release to SEGA for review. + * + * 0.51 + * Added other interfaces to control effects, volume, optical-out etc. + * Cleanup and added more information on the wave playback interfaces. + * + * 0.52 + * Added correlation between HASYNTHPARAMSEXT enumerators with SoundFont parameters. + * + * 0.53 + * Change name to segaapi.h (was WaveOutSegaAPI.h) as the API covers more than + * just wave out. + * Add prefix SEGAAPI_ to each of the functions to avoid names conflict. + * Change HAERR_ to SEGAERR_, HA_SUCCESS to SEGA_SUCCESS + * + * 0.54 + * Added more documentation. + * + * 0.80 + * Added SetLastStatus and GetLastStatus functions. + * Change version to 0.80 to indicate close to final. + * + * 0.9 + * Made the header file both gcc and g++ compliance as per request. + * + * 0.91 + * Updated voice priority description. + * + * 0.92 + * Added SEGAAPI_Reset, and updated CreateBuffer() to support synthesizer buffer. + * + * 0.93 + * Added SEGAAPI_GetSendRouting and SEGAAPI_GetSetLevel per request. + * + * 0.94 + * Changed default send levels in SEGAAPI_CreateBuffer to 0. + * + * 1.00 + * Added SEGAAPI_Init() and SEGAAPI_Exit() per request. + * + * 1.01 + * Updated SEGAAPI_SetReleaseState() document. + * + * 1.02 +* Added SEGAAPI_SetSynthParamMultiple(0 and SEGAAPI_GetSynthParamMultiple(). + * Updated SEGAAPI_CreateBuffer() for user-mode buffer support. + * + **************************************************************************** + * Released under NDA. + * + * This document has been reviewed by SEGA. + **************************************************************************** + */ + +#ifndef __SEGAAPI_H +#define __SEGAAPI_H + +// INCLUDES +#include "segadef.h" +#include "segaerr.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The following defines SEGA custom EAX40 properties. + */ + +// {A7FEEC3F-2BFD-4a40-891F-7423E38BAC1F} +DEFINE_GUID(EAXPROPERTYID_EAX40_SEGA_Custom, +0xa7feec3f, 0x2bfd, 0x4a40, 0x89, 0x1f, 0x74, 0x23, 0xe3, 0x8b, 0xac, 0x1f); + +// SEGA custom EAX40 properties +/* + * The only property now is to switch the FX returns for + * FXSlot2 and FXSlot3 when non-reverb is loaded to these slots. + * + * EAXSEGA_STEREO_RETURN + * ulDataSize = CTDWORD + * value = 0 denotes route to front L/R (default) + * value = 1 denotes route to Rear L/R + */ +typedef enum +{ + EAXSEGA_STEREO_RETURN_FX2 = 0, + EAXSEGA_STEREO_RETURN_FX3 = 1 +} EAXSEGA_PROPERTY; + +/** + * The following defines all of the messages wave output clients + * can receive as part of their callback routines. + */ +typedef enum { + HAWOS_RESOURCE_STOLEN = 0, + HAWOS_NOTIFY = 2 +} HAWOSMESSAGETYPE; + + +/* + * The Playback status. + */ +typedef enum { + HAWOSTATUS_STOP, /* The voice is stopped */ + HAWOSTATUS_ACTIVE, /* The voice is playing */ + HAWOSTATUS_PAUSE, /* The voice is paused */ + HAWOSTATUS_INVALID = -1 /* Invalid state */ +} HAWOSTATUS; + + +/* + * dwFlags use in CreateBuffer. + */ +#define HABUF_SYNTH_BUFFER 0x00000001 // indiate to create a synth buffer +#define HABUF_ALLOC_USER_MEM 0x00000002 // indiate that caller allocate memory +#define HABUF_USE_MAPPED_MEM 0x00000003 // indiate that caller allocate memory + +/* + * The HAWOSEFORMAT structure is used to change the format of an output client. + */ + +#ifndef __HAWAVE_H +#define HASF_UNSIGNED_8PCM 0x0004 /* Unsigned (offset 128) 8-bit PCM */ +#define HASF_SIGNED_16PCM 0x0020 /* Signed 16-bit PCM */ +#endif + +typedef struct { + CTDWORD dwSampleRate; /* The sample rate the client desires (in Hz) */ + CTDWORD dwSampleFormat; /* The sample format the client will use */ + CTDWORD byNumChans; /* The number of samples in the sample + * frame (1 = mono, 2 = stereo). */ +} HAWOSEFORMAT; + + + +/* + * HAWOSEMAPDATA contains + */ +typedef struct { + CTDWORD dwSize; /* Supply by caller. Size (in bytes) of the valid sample data */ + CTDWORD dwOffset; /* Return by driver. Offset of buffer where the the first valid sample should be written to */ + CTHANDLE hBufferHdr; /* Return by driver. Memory address that user-space application can access. */ +} HAWOSEMAPDATA; + + +/* + * The HAWOSEBUFFERCONFIG structure is used to describe how an input or + * output buffer client wishes to configure the device when it opens it. + */ +typedef struct { + CTDWORD dwPriority; /* The priority with which the voices + * should be allocated. This is used + * when voices need to be ripped off. */ + CTDWORD dwSampleRate; /* The sample rate the voice desires */ + CTDWORD dwSampleFormat; /* The sample format the voice will use */ + CTDWORD byNumChans; /* The number of samples in the sample + * frame. (1 = mono, 2 = stereo). */ + CTDWORD dwReserved; /* Reserved field */ + CTHANDLE hUserData; /* User data */ + HAWOSEMAPDATA mapData; /* The sample memory mapping for the buffer. */ +} HAWOSEBUFFERCONFIG; + + + +/** + * Default values + */ +#define HAWOSEVOL_MAX 0xFFFFFFFF /* Maximum volume; no attenuation */ + + +/** + * Since Tina has up to 64- voices, voice priorities typically ranging + * from 0 to 63, where 0 is lower priority (more likely to get ripped off) + * than 63. + * + * Set voice priority to HAWOSEP_MAXIMUM if a voice must never get ripped + * off under any circumstances. + */ +#define HAWOSEP_MINIMUM 0 +#define HAWOSEP_MAXIMUM 0xFFFFFFFF + + +/** @brief Routing List + * + * voice sends routing to speakers or effects ports. + * + */ +#define HAWOSE_UNUSED_SEND 0xFFFF0001 + + +typedef enum HAROUTING{ + HA_UNUSED_PORT=HAWOSE_UNUSED_SEND, + + // Dry multi-channel outputs + HA_FRONT_LEFT_PORT =0, + HA_FRONT_RIGHT_PORT=1, + HA_FRONT_CENTER_PORT=2, + HA_LFE_PORT=3, + HA_REAR_LEFT_PORT=4, + HA_REAR_RIGHT_PORT=5, + + // effect outputs + HA_FXSLOT0_PORT=10, + HA_FXSLOT1_PORT=11, + HA_FXSLOT2_PORT=12, + HA_FXSLOT3_PORT=13 + +} HAROUTING; + + +/** + * The following defines SPDIF-Out sampling rate. + */ +typedef enum { + HASPDIFOUT_44_1KHZ=0, + HASPDIFOUT_48KHZ, + HASPDIFOUT_96KHZ +} HASPDIFOUTRATE; + + +/** + * The following defines inputs and outputs of SEGA sound board. + */ +typedef enum HAPHYSICALIO { + // analog outputs + HA_OUT_FRONT_LEFT =0, + HA_OUT_FRONT_RIGHT=1, + HA_OUT_FRONT_CENTER=2, + HA_OUT_LFE_PORT=3, + HA_OUT_REAR_LEFT=4, + HA_OUT_REAR_RIGHT=5, + + // optical Outputs + HA_OUT_OPTICAL_LEFT=10, + HA_OUT_OPTICAL_RIGHT=11, + + // Line In + HA_IN_LINEIN_LEFT=20, + HA_IN_LINEIN_RIGHT=21 + +}HAPHYSICALIO ; + + +/** @brief Synth parameters enumeration list + * + * This table defines the most common (and hardware-supported) + * control routings in Real World Unit. + * + * Refers to DLS spec or SoundFont spec for details of these Parameters, + * their units and their ranges. + */ +typedef enum HASYNTHPARAMSEXT { + HAVP_ATTENUATION, ///< 0, 0x00, initialAttenuation + HAVP_PITCH, ///< 1, 0x01, fineTune + coarseTune * 100 + HAVP_FILTER_CUTOFF, ///< 2, 0x02, initialFilterFc + HAVP_FILTER_Q, ///< 3, 0x03, initialFilterQ + HAVP_DELAY_VOL_ENV, ///< 4, 0x04, delayVolEnv + HAVP_ATTACK_VOL_ENV, ///< 5, 0x05, attackVolEnv + HAVP_HOLD_VOL_ENV, ///< 6, 0x06, holdVolEnv + HAVP_DECAY_VOL_ENV, ///< 7, 0x07, decayVolEnv + HAVP_SUSTAIN_VOL_ENV, ///< 8, 0x08, sustainVolEnv + HAVP_RELEASE_VOL_ENV, ///< 9, 0x09, releaseVolEnv + HAVP_DELAY_MOD_ENV, ///< 10, 0x0A, delayModEnv + HAVP_ATTACK_MOD_ENV, ///< 11, 0x0B, attackModEnv + HAVP_HOLD_MOD_ENV, ///< 12, 0x0C, holdModEnv + HAVP_DECAY_MOD_ENV, ///< 13, 0x0D, decayModEnv + HAVP_SUSTAIN_MOD_ENV, ///< 14, 0x0E, sustainModEnv + HAVP_RELEASE_MOD_ENV, ///< 15, 0x0F, releaseModEnv + HAVP_DELAY_MOD_LFO, ///< 16, 0x10, delayModLFO + HAVP_FREQ_MOD_LFO, ///< 17, 0x11, freqModLFO + HAVP_DELAY_VIB_LFO, ///< 18, 0x12, delayVibLFO + HAVP_FREQ_VIB_LFO, ///< 19, 0x13, freqVibLFO + HAVP_MOD_LFO_TO_PITCH, ///< 20, 0x14, modLfoToPitch + HAVP_VIB_LFO_TO_PITCH, ///< 21, 0x15, vibLfoToPitch + HAVP_MOD_LFO_TO_FILTER_CUTOFF, ///< 22, 0x16, modLfoToFilterFc + HAVP_MOD_LFO_TO_ATTENUATION, ///< 23, 0x17, modLfoToVolume + HAVP_MOD_ENV_TO_PITCH, ///< 24, 0x18, modEnvToPitch + HAVP_MOD_ENV_TO_FILTER_CUTOFF ///< 25, 0x19, modEnvToFilterFc + +} HASYNTHPARAMSEXT; + +#ifndef __SYNTHPARAMSET_ +# define __SYNTHPARAMSET_ +typedef struct SynthParamSetExt { + HASYNTHPARAMSEXT param; + CTLONG lPARWValue; +} SynthParamSet; +#endif + + +/* +How this SYNTH PARAMS EXT maps to Sega API requests: + + HAVP_ATTENUATION, SetVolume() + HAVP_PITCH, SetPitch() + HAVP_FILTER_CUTOFF, SetFilter() + HAVP_FILTER_Q, SetFilter() + HAVP_DELAY_VOL_ENV, SetEG() + HAVP_ATTACK_VOL_ENV, SetEG() + HAVP_HOLD_VOL_ENV, SetEG() + HAVP_DECAY_VOL_ENV, SetEG() + HAVP_SUSTAIN_VOL_ENV, SetEG() + HAVP_RELEASE_VOL_ENV, SetEG() + HAVP_DELAY_MOD_ENV, SetEG() + HAVP_ATTACK_MOD_ENV, SetEG() + HAVP_HOLD_MOD_ENV, SetEG() + HAVP_DECAY_MOD_ENV, SetEG() + HAVP_SUSTAIN_MOD_ENV, SetEG() + HAVP_RELEASE_MOD_ENV, SetEG() + HAVP_DELAY_MOD_LFO, SetLFO() + HAVP_FREQ_MOD_LFO, SetLFO() + HAVP_DELAY_VIB_LFO, SetLFO() + HAVP_FREQ_VIB_LFO, SetLFO() + HAVP_MOD_LFO_TO_PITCH, SetLFO() + HAVP_VIB_LFO_TO_PITCH, SetLFO() + HAVP_MOD_LFO_TO_FILTER_CUTOFF, SetLFO() + HAVP_MOD_LFO_TO_ATTENUATION, SetLFO() + HAVP_MOD_ENV_TO_PITCH, SetEG() + HAVP_MOD_ENV_TO_FILTER_CUTOFF, SetEG() + +*/ + + + +/* + * Interfaces expose. These interfaces will be exposed in user mode. + * + * Note: + * 1. hHandle that passes into these functions is An opaque identifier + * obtained from CreateBuffer. + * + * 2. A mono buffer uses one voice. A stereo buffer uses two voices. + * + */ + + +/*********************************************************** + * @section + * API for playback operation controls. + * + */ + +/** + * Starts sample playback of a buffer. + * Playback position is not modified when Play is called and will + * start incrementing from its previous value at the sample + * rate. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns SEGA_SUCCESS if playback can start. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_Play(CTHANDLE hHandle); + + +/** + * Halts playback and freezes the current counter at its last + * value. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns SEGA_SUCCESS if playback was successfully paused. + * Otherwise, returns an appropriate error code. + */ +SEGASTATUS SEGAAPI_Pause(CTHANDLE hHandle); + + +/** + * Stops playback and resets the sample counter. + * + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns SEGA_SUCCESS if playback was successfully paused. + * Otherwise, returns an appropriate error code. + * + */ +SEGASTATUS SEGAAPI_Stop(CTHANDLE hHandle); + +SEGASTATUS SEGAAPI_PlayWithSetup(CTHANDLE hHandle); + +/** + * Returns a current playback status of a buffer. + * + * CALL LEVELS: DPC, PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * One of the playback status defined in the HAWOSTATUS enumration type. + * If the returned status is HAWOSTATUS_INVALID, use GetLastStatus() to check the error code. + */ +HAWOSTATUS SEGAAPI_GetPlaybackStatus(CTHANDLE hHandle); + + + + +/*********************************************************** + * @section + * API for playback format controls. + * + */ + +/** + * Changes the buffer's format to the values specified in + * the new format pFormat. In some cases, it is possible that + * an attempt to change the configuration may fail, as the change may + * require more resources than were previously allocated. + * + * The playback buffer configuration may not be changed while the buffer is + * playing. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param pFormat + * The new format to change to. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + * + * @retval SEGAERR_PLAYING if the buffer is currently playing + * @retval SEGAERR_BAD_POINTER if pFormat is NULL + * @retval SEGAERR_NO_RESOURCES if insufficient resources are available + * @retval SEGAERR_BAD_CONFIG if something in the given configuration is + * invalid. + */ +SEGASTATUS SEGAAPI_SetFormat(CTHANDLE hHandle, HAWOSEFORMAT *pFormat); + + +/** + * Returns the current format of the buffer. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param pFormat + * Pointer to an address where the current format to return to. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_GetFormat(CTHANDLE hHandle, HAWOSEFORMAT *pFormat); + + +/** + * Changes the playback sample rate for the current client to the + * value specified. The new value only pertains to this buffer. + * If hardware cannot support changing sample rates for individual + * buffers, it can return an error in response to this routine. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwSampleRate + * The desired sample rate. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetSampleRate(CTHANDLE hHandle, CTDWORD dwSampleRate); + + +/** + * Returns the current sample rate. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns the current sample rate. + * If the returned value is 0, use GetLastStatus() to check the error code. + */ +CTDWORD SEGAAPI_GetSampleRate(CTHANDLE hHandle); + + + +/*********************************************************** + * @section + * API for Voice priority management. + * + */ + + /** + * Changes the buffer's priority to the specified value. + * + * If all the voices are set to HAWOSEP_MAXIMUM (0xFFFFFFFF) priority, + * CreateBuffer() call will return failure when running out of voices. + * + * CALL LEVEL: DPC, PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwPriority + * The new priority for the buffer. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetPriority(CTHANDLE hHandle, CTDWORD dwPriority); + + +/** + * Returns the buffer's current priority. + * + * CALL LEVEL: DPC, PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns the buffer's current priority. + * Note that returned value is also set to 0 if error invoked this function. + */ +CTDWORD SEGAAPI_GetPriority(CTHANDLE hHandle); + + +/*********************************************************** + * @section + * API for storing an User-defined data. + * + */ + +/** + * Stores a handle to user-defined data defined by the client structure. + * This allows caller to associate caller-specific data. The caller is + * responsible for managing this data area. + * + * Note that caller can specify an user-defined data in the CreateBuffer() + * call. Caller can use SetUserData() to update the user-defined data. + * + * CALL LEVELS: DPC, PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param hUserData + * A handle to user-defined data. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetUserData(CTHANDLE hHandle, CTHANDLE hUserData); + + +/** + * Returns the last user-defined data set by the caller. + * + * CALL LEVELS: DPC, PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns the user-defined data. + * Note that returned value is set to 0 if error invoked this function. + */ +CTHANDLE SEGAAPI_GetUserData(CTHANDLE hHandle); + + +/*********************************************************** + * @section + * API for Send routing and Send Level controls. + * + */ + +/** + * Changes the destination to which a channel send is connected. + * Each channel has the possibility of supporting multiple sends. + * For Tina chip, each channel has seven sends. + * Each of these sends can be connected to a destination. + * + * Note that it is invalid to have more than one sends routed to + * a same destination. For example, if send 0 is previously routed + * to front-left, send 0 will need to be disconnected before another + * send can route to front-left. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwChannel + * The channel to reroute. + * + * @param dwSend + * The send number being addressed. + * + * @param dwDest + * The destination to which the send should be connected. + * If the send doesn't need to be connected to anything, + * specify HAWOSE_UNUSED_SEND. See HAROUTING for the details + * of the destination enumeration list. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + * + * @retval SEGAERR_UNSUPPORTED if channels can't be rerouted. + * @retval SEGAERR_INVALID_CHANNEL if the specified channel isn't in use. + * @retval SEGAERR_INVALID_SEND if the specified send isn't supported. + */ +SEGASTATUS SEGAAPI_SetSendRouting(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend, + HAROUTING dwDest); + +/** + * Returns the destination of which a channel send is connected to. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwChannel + * The channel requested. + * + * @param dwSend + * The send number being addressed. + * + * @return + * Returns the current destination. + * Note that returned value is set to HA_UNUSED_PORT if error invoked this function. + */ +HAROUTING SEGAAPI_GetSendRouting(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend); + +/** + * Sets the output level of a particular send on a channel to the specified + * level. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwChannel + * The channel whose output level is to be set. + * + * @param dwSend + * The send being addressed. + * + * @param dwLevel + * The output level. Output levels are specified + * in linear values. A value of 0xFFFFFFFF indicates + * full on (0 attenuation). A value of 0x0 indicates + * infinite attenuation. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + * + * @retval SEGAERR_UNSUPPORTED if the device doesn't support channel levels in + * general. + * @retval SEGAERR_INVALID_CHANNEL if the specified channel isn't valid. + * @retval SEGAERR_INVALID_SEND if the specified send isn't valid. + */ +SEGASTATUS SEGAAPI_SetSendLevel(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend, + CTDWORD dwLevel); + +/** + * Returns the output level of a particular send on a channel. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwChannel + * The channel requested. + * + * @param dwSend + * The send number being addressed. + * + * @return + * Returns the current send level. + * Note that returned value is set to 0 if error invoked this function. + */ +CTDWORD SEGAAPI_GetSendLevel(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend); + + + +/*********************************************************** + * @section + * API for volume level controls. + * + */ + +/** + * Sets the volume to a specific linear value. Volumes are specified + * as fractional fixed-point linear values between HAWOSEVOL_MAX (0xFFFFFFFF) + * and 0x0. The dwVolume is specified in linear increments from 0 to 1 + * (actually to 65535 divided by 65536). A 0 value represents 96db of attenuation, + * while a 1 value represents full volume. Default is full volume. + * + * Volume is a global value and is applied pre-send. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwChannel + * The channel to change. + * + * @param dwVolume + * The new volume level to change to. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + * + * @retval SEGAERR_UNSUPPORTED if the device can't change volume. + * @retval SEGAERR_INVALID_CHANNEL if the given send isn't valid. + */ +SEGASTATUS SEGAAPI_SetChannelVolume(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwVolume); + +/** + * Returns the current volume level for the requested channel. + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwChannel + * The channel requested. + * + * @return + * Returns the current volume. + * Note that returned value is set to 0 if error invoked this function. + */ +CTDWORD SEGAAPI_GetChannelVolume(CTHANDLE hHandle, CTDWORD dwChannel); + + + +/*********************************************************** + * @section * API for playback position controls. + * + */ + +/** + * Changes the buffer position pointer from which hardware + * is fetching samples. Changes take place immediately and + * can be made while playback is occurring. + * + * CALL LEVELS: DPC, PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwPlaybackPos + * The buffer position (IN BYTES) where the playback + * pointer should be moved. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetPlaybackPosition(CTHANDLE hHandle, CTDWORD dwPlaybackPos); + + +/** + * Returns the position in the buffer (IN BYTES) where the + * hardware is currently playing samples from. + * + * CALL LEVELS: DPC, PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns the current playback position. + * Note that returned value is set to 0 if error invoked this function. + */ +CTDWORD SEGAAPI_GetPlaybackPosition(CTHANDLE hHandle); + + +/*********************************************************** + * @section + * API for buffer update, loop regions and notification controls. + * + */ + +/** + * This function sets the frequency at which a callback will be generated. + * The callback will be invoked periodically at a fixed interval + * specified by the dwFrameCount. dwFrameCount is in the units of + * sample frames. + * + * This notification method is typically used for ring buffer that need + * periodic notification to update the ring buffer data. + * + * Note that callback execution is scheduled at the later time (DPC), not at + * the interrupt time. + * + * + * CALL LEVELS: PASSIVE + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwFrameCount + * The frequency, in sample frames, at which the the notification is invoked. + * Specifying a value of zero cancels the callback. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetNotificationFrequency(CTHANDLE hHandle, CTDWORD dwFrameCount); + +/** + * This function can be used to set a notification point in the ring + * buffer. Whenever the play position passes over a notification point + * the device will schedule a callback to the function indicated when + * the ring buffer was created. + * + * Note that callback execution is scheduled at the later time (DPC), not at + * the interrupt time. + * + * CALL LEVELS: PASSIVE, DPC + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwBufferOffset + * The offset (in bytes) in the buffer where the notification + * point is to be set. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetNotificationPoint(CTHANDLE hHandle, CTDWORD dwBufferOffset); + +/** + * Removes a previously set notification point. + * + * CALL LEVELS: PASSIVE, DPC + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwBufferOffset + * The offset (in bytes) of the notification point to + * remove. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_ClearNotificationPoint(CTHANDLE hHandle, CTDWORD dwBufferOffset); + + +/** + * Sets the start loop offset. The start loop offset controls where + * the play pointer will jump when it crosses the End Loop Offset. + * There is no requirement that the start loop offset preceed the + * End Loop Offset. It is illegal to set Start Loop Offset and + * End Loop Offset to the same value, however, and in general it is + * a bad idea to have the difference between the two be less than + * 16 samples. + * + * GetStartLoopOffset() just returns the current start loop offset + * value and may be called at any interrupt level. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwOffset + * The offset in bytes from the beginning of the buffer of + * the loop start point. + * + * @return + * Returns SEGA_SUCCESS if the loop offset is changed successful. + * Otherwise, returns an appropriate error code. + */ +SEGASTATUS SEGAAPI_SetStartLoopOffset(CTHANDLE hHandle, CTDWORD dwOffset); + + +/** + * Returns the current start loop offest. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns the current start loop offset. + * Note that returned value is set to 0 if error invoked this function. + */ +CTDWORD SEGAAPI_GetStartLoopOffset(CTHANDLE hHandle); + + +/** + * Sets the End Loop Offset position. When the play pointer crosses + * the End Loop Offset it will jump to the Start Loop Offset + * position if buffer is in looping state (bDoContinuousLooping + * is set to TRUE). + * + * GetEndLoopOffset() just returns the current value and may be called + * at any interrupt level. + * + * Note that EndLoopOffset must not be larger than EndOffset. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwOffset + * An offset in bytes from the beginning of the buffer, + * dwOffset specifies the location for the End Loop point. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetEndLoopOffset(CTHANDLE hHandle, CTDWORD dwOffset); + + +/** + * Returns the current end loop offest. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns the current end loop offest. + * Note that returned value is set to 0 if error invoked this function. + */ +CTDWORD SEGAAPI_GetEndLoopOffset(CTHANDLE hHandle); + + +/** + * Sets the End Offset position. When the play pointer crosses + * the End Offset (assuming the buffer isn't currently looping) + * the buffer will halt. + * + * Only change the End offset position when buffer is not at + * HAWOSTATUS_ACTIVE state. End Offset must be sample frame aligned. + * For example, 16-bit 1 channel is WORD aligned, 16-bit 2 channel + * is DWORD aligned. + * + * Note that EndOffset must not be larger than pConfig->mapdata.dwSize + * specified in the CreateBuffer(). + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwOffset + * An offset in bytes from the beginning of the buffer, + * dwOffset specifies the location for the End point. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetEndOffset(CTHANDLE hHandle, CTDWORD dwOffset); + +/** + * Returns the current end offest. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns the current end offest. + * Note that returned value is set to 0 if error invoked this function. + */ +CTDWORD SEGAAPI_GetEndOffset(CTHANDLE hHandle); + +/** + * Allows the user to control whether the voice loops back to the + * Start Loop Offset when it crosses the End Loop Point or whether + * it goes into the release phase (i.e. post end loop). Note that + * setting the loop state doesn't actually cause the device + * to transition to the stopped state. + * + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param bDoContinuousLooping + * If TRUE, the buffer will loop from start-loop to end + * loop and back again. If FALSE, loop points are ignored + * and buffer will play until the sample end, as programmed + * with SetEndOffset. This may be programmed when as the buffer + * is playing. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetLoopState(CTHANDLE hHandle, CTBOOL bDoContinuousLooping); + +/** + * Returns the current loop status. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns TRUE if it is in loop state. Otherwise, returns FALSE. + * Note that returned value is set to FALSE if error invoked this function. + */ +CTBOOL SEGAAPI_GetLoopState(CTHANDLE hHandle); + + +/** + * Advises the driver code that some portion of the buffer + * has been written with new data. This method is required for devices + * which don't support a memory-mapped ring buffer and allows the + * underlying software to perform any necessary copying or format + * conversion. + * + * The caller should call this function *after* they have filled + * the data into the ring buffer that they passed + * to the driver in the CreateBuffer() call. + * + * Although this routine takes it values in bytes, the caller + * is responsible for insuring that the starting offset and + * length are a integer multiple of the sample size. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwStartOffset + * The offset of the first byte in the buffer which + * has changed (between 0 and bufferSize-1) + * @param dwLength + * The number of bytes which have changed. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_UpdateBuffer(CTHANDLE hHandle, CTDWORD dwStartOffset, CTDWORD dwLength); + + +/*********************************************************** + * @section + * Low level API to control Synth buffer parameters + * + */ + +/** + * Sets and stores a synthesizer parameter, in Perceptually-Additive Real-World (PARW) units + * + * The parameter is applied for mono buffer only. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param param + * The parameter to apply + * + * @param lPARWValue + * The value in PARW units + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetSynthParam(CTHANDLE hHandle, HASYNTHPARAMSEXT param, CTLONG lPARWValue); + + +/** + * Returns the most recent call to SetSynthParam() in PARW units. This is the cache value + * of the most recent PARW value set by SetSynthParam(). If the parameter has not been set + * before, this function will return 0. + * + * The parameter is applied for mono buffer only. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param param + * The parameter to retrieve + * + * @return + * The returned value in PARW units. + * Note that returned value is set to -1 if error invoked this function. + */ +CTLONG SEGAAPI_GetSynthParam(CTHANDLE hHandle, HASYNTHPARAMSEXT param); + + +/** + * Sets and stores an array of synthesizer parameters, in Perceptually-Additive Real-World (PARW) units. + * + * The parameter is applied for mono buffer only. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwNumParams + * Number of parameters to apply + * + * @param pSynthParams + * Pointer to the Synth Parameters array to apply. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetSynthParamMultiple(CTHANDLE hHandle, CTDWORD dwNumParams, SynthParamSet *pSynthParams); + + +/** + * Retrieves an array of synthesizer parameters, in Perceptually-Additive Real-World (PARW) units. + * + * The parameter is applied for mono buffer only. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param dwNumParams + * Number of parameters to retrieve. + * + * @param pSynthParams + * Pointer to the Synth Parameters array to retrieve. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_GetSynthParamMultiple(CTHANDLE hHandle, CTDWORD dwNumParams, SynthParamSet *pSynthParams); + + +/** + * Set the voice into the release phase of the volume envelope engines when set to TRUE. + * This will automatically stop the voice when the voice reaches end of release phase. + * + * CALL LEVELS: DPC, PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param bSet + * TRUE for enter releaes phase + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetReleaseState(CTHANDLE hHandle, CTBOOL bSet); + + + +/*********************************************************** + * @section + * Playback buffers (voices) callback notification function. + * + */ + +/** + * A callback function of this type is passed into the + * CreateBuffer() function and gets invoked when a WaveOutBuffer + * client needs to notify the OS of some event. Currently, the following event + * types are defined: + * + * HAWOS_RESOURCE_STOLEN -- Indicates the resources used to + * play the audio have been stolen. + * + * HAWOS_NOTIFY -- Indicates that the current play position + * has passed over one of the notification points set + * with SetNotificationPoint. + * + * CALL LEVELS: The callback is invoked at DPC level. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @param HAWOSMESSAGETYPE message + * The callback message. + */ +typedef void (*HAWOSEGABUFFERCALLBACK)(CTHANDLE hHandle, + HAWOSMESSAGETYPE message); + + +/*********************************************************** + * @section + * API to create playback buffers (voices). + * + * This is the entry call to obtain the voice handle + * and perform further voice operations. + */ + +/** + * Creates a new buffer (voice) of the output device using + * the configuration specified in pConfig. + * + * There are two types of buffers: normal buffer and synthesizer buffer. + * Typically, a buffer is created as normal buffer. Synthesizer buffer + * is used if a buffer requires synthesizer type of parameter controls. + * + * Caller specifies the configuration to create in pConfig structure. + * Driver will allocate memory and hardware resources needed for this + * voice creation. If successful, the pConfig->mapdata.hBufferHdr contains + * the memory address that user-space application can access. Caller + * should write the valid sound data into this memory buffer. + * + * Caller can indicate to the sound driver to use the caller allocated sound + * data memory buffer for efficiency purpose if the same buffer is re-used frequently. + * In such a case, caller needs to provide the sound buffer address in the + * pConfig->mapdata.hBufferHdr and sets the HABUF_ALLOC_USER_MEM bit + * of dwFlags parameter. Caller needs to ensure that the sound buffer + * memory is page-aligned and locked. + * + * pConfig->mapdata.dwSize must be sample frame aligned. For example, + * 16-bit 1 channel is WORD aligned, 16-bit 2 channel is DWORD aligned. + * For more efficient memory management, pConfig->mapdata.dwSize is + * recomended to be page aligned. + * + * The size of the ring buffer is fixed throughout the lifetime of the + * buffer until it is destroyed. During the lifetime of the buffer, + * caller can periodically update the data of the buffer if necessary or + * modulate the buffer with SetSynthParam or SetSynthParamMultiple functions + * if the buffer is created as synthesizer buffer. + * + * If all the voices are currently in use, CreateBuffer will perform voice-stealing + * to fulfill the request. Note that voice stealing may fail if all voices that are + * currently in use are set to HAWOSEP_MAXIMUM priority. + * + * The followings are default values for a newly created buffer: + * - 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. + * - EndLoopOffset and EndOffset are set to pConfig->mapdata.dwSize. + * - No loop. + * - Buffer is in the stop state. + * - Play position is set to 0. + * + * CALL LEVELS: PASSIVE. + * + * @param pConfig + * A pointers to configuration structures containing + * information about how the clients should be opened. + * + * @param pCallback + * A pointer to the callback function. + * + * @param dwFlags + * HABUF_SYNTH_BUFFER bit when set indicates synthesizer buffer. + * HABUF_ALLOC_USER_MEM bit when set indicates caller allocate sound data memory buffer. + * + * @param phHandle + * A pointer to a memory address where the token of new client identifier + * should be placed. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + * + * @retval SEGAERR_BAD_POINTER if either the pConfig or phHandle pointers are + * NULL. + * @retval SEGAERR_OUT_OF_MEMORY if buffer size requested in pConfig cannot be + * allocated. + * @retval SEGAERR_BAD_CONFIG if the device can't support the configuration + * requested. + * @retval SEGAERR_NO_RESOURCES if no resources are available for creating + * the device. Generally, increasing the client's priority + * will allow a subsequent creation request to succeed. + */ +SEGASTATUS SEGAAPI_CreateBuffer(HAWOSEBUFFERCONFIG * pConfig, + HAWOSEGABUFFERCALLBACK pCallback, + CTDWORD dwFlags, + CTHANDLE *phHandle); + + +/** + * Destroys the buffer previously created with CreateBuffer(). + * This will free all the resources previously allocated to this buffer. + * + * This function will stop the buffer if it is not at the stop state before + * freeing all the resources. + * + * CALL LEVELS: PASSIVE. + * + * @param hHandle + * An opaque identifier obtained from CreateBuffer. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_DestroyBuffer(CTHANDLE hHandle); + + + + + +/*********************************************************** + * @section + * API to control effect slots and effects parameters. + * + * Refers to the EAX4 Programmer's Guide for the details of + * controlling FX Slots and controlling Effect parameters. + * + * Only the EAX4 FX Slots and Effect Parameters property controls + * will be implemented for this project. + * + * Need to add property to switch the FX returns for + * FXSlot2 and FXSlot3 when non-reverb is loaded to these slots + * as per SEGA request. + */ + +/** + * Sets global EAX property. + * + * This function sets the EAX4 FX Slots and Effect Parameters property + * controls as defined in EAX4 EAX4 Programmer's Guide. + * + * @param guid + * EAX Object GUID + * + * @param ulProperty + * Property enumeration value of each object + * + * @param pData + * A pointer to a memory address where the data will be accessed + * + * @param ulDataSize + * An unsigned integer indicating the size of the data pointed to by the pData. + * + * @return + * Returns TRUE if successful. Otherwise, returns FALSE. + */ +CTBOOL SEGAAPI_SetGlobalEAXProperty(GUID *guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize); + +/** + * Gets global EAX property. + * + * This function gets the EAX4 FX Slots and Effect Parameters property + * controls as defined in EAX4 EAX4 Programmer's Guide. + * + * @param guid + * EAX Object GUID + * + * @param ulProperty + * Property enumeration value of each object + * + * @param pData + * A pointer to a memory address where the data will be accessed + * + * @param ulDataSize + * An unsigned integer indicating the size of the data pointed to by the pData. + * + * @return + * Returns TRUE if successful. Otherwise, returns FALSE. + */ +CTBOOL SEGAAPI_GetGlobalEAXProperty(GUID *guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize); + + +/*********************************************************** + * @section + * API to control SPDIF Output channel status, sampling rate + * and output routing matrix. + * + */ + +/** + * Sets SPDIF Out channel status. + * + * @param dwChannelStatus + * Channel Status + * + * @param dwExtChannelStatus + * Extended Channel Status + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetSPDIFOutChannelStatus( + CTDWORD dwChannelStatus, + CTDWORD dwExtChannelStatus); + +/** + * Gets SPDIF Out channel status. + * + * @param pdwChannelStatus + * Pointer to address where Channel Status is returned to. + * + * @param pdwExtChannelStatus + * Pointer to address where Extended Channel Status is returned to. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_GetSPDIFOutChannelStatus( + CTDWORD *pdwChannelStatus, + CTDWORD *pdwExtChannelStatus); + + +/** + * Sets the SPDIF Out sampling rate. This function also updates the + * SPDIF-Out channel status to reflect the correct sampling rate. + * The default SPDIF Out sampling rate is 48KHz. + * + * @param dwSamplingRate + * Sampling rate enumeration type + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetSPDIFOutSampleRate(HASPDIFOUTRATE dwSamplingRate); + +/** + * Gets SPDIF Out sampling rate + * + * @return + * Returns sampling rate enum. + * Note that returned value is set to HASPDIFOUT_48KHZ if error invoked this function. + */ +HASPDIFOUTRATE SEGAAPI_GetSPDIFOutSampleRate(void); + + +/** + * Sets SPDIF Out channel routing. + * + * @param dwChannel + * The channel to route. + * 0 for Left channel, 1 for right channel. + * + * @param dwSource + * The source to which the channel should be received signal from. + * If the channel doesn't need to be connected to anything, + * specify HA_UNUSED_PORT. + * HA_FXSLOTx_PORT is not a valid source. Routes from these ports + * will return failure. + * + * See HAROUTING for the details of the source enumeration list. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetSPDIFOutChannelRouting( + CTDWORD dwChannel, + HAROUTING dwSource); + + /** + * Gets SPDIF Out channel routing + * + * @param dwChannel + * The channel to route. + * 0 for Left channel, 1 for right channel. + * + * @return + * The source to which the channel is received signal from. + * Note that returned value is set to HA_UNUSED_PORT if error invoked this function. + * + * See HAROUTING for the details of the source enumeration list. + */ +HAROUTING SEGAAPI_GetSPDIFOutChannelRouting(CTDWORD dwChannel); + + + +/*********************************************************** + * @section + * API to control global inputs and outputs volume. + * For outputs, these volume controls are post-routing. + */ + +/** + * Sets the volume to a specific linear value. Volumes are specified + * as fractional fixed-point linear values between HAWOSEVOL_MAX (0xFFFFFFFF) + * and 0x0. Note that only the upper word is effective. + * + * @param dwPhysIO + * The Physical IO being addressed. + * + * @param dwVolume + * The new volume level. + * + * @return + * Returns SEGA_SUCCESS if successful. Otherwise, returns an appropriate + * error code. + */ +SEGASTATUS SEGAAPI_SetIOVolume(HAPHYSICALIO dwPhysIO, CTDWORD dwVolume); + +/** + * Returns the current volume level for the requested physical IO. + * + * @param dwPhysIO + * The Physical IO being addressed. + * + * @return + * The current volume. + * Note that returned value is set to 0xffffffff if error invoked this function. + */ +CTDWORD SEGAAPI_GetIOVolume(HAPHYSICALIO dwPhysIO); + +/** + * Sets the last status code manually. + * + * Typically, this is use to reset the last status code. + * Note that the last status code will be reset whenever a function is invoked. + * + * @param LastStatus + * The last status code to change to. + * + * @return + * None. + */ +void SEGAAPI_SetLastStatus(SEGASTATUS LastStatus); + + +/** + * Returns the last status code for the function that just invoked. + * The last status code will be reset whenever a function is invoked. + * Therefore, The last status code should be checked immediately after a function + * is invoked. + * + * For functions that return SEGASTATUS, caller can check the return code + * immediately without needing to call GetLastStatus function. + * + * @return + * The SEGASTATUS code. + */ +SEGASTATUS SEGAAPI_GetLastStatus(void); + + +/** + * Resets the driver to its default states. + * + * This includes but not limited to the followings: + * - Stop and destroy all the currently playing buffers. All previous buffer + * handles are no longer valid after returning from this call. + * - Resets all volume levels to its default. + * - Resets EAX property values to their defaults. + * - Resets SPDIF Out sampling rate and routing to its default. + * + * + * @return + * The SEGASTATUS code. + */ +SEGASTATUS SEGAAPI_Reset(void); + + +/** + * Initializes the SEGAAPI Library. + * + * This must be the first function to call before using any of the SEGAAPI functions. + * + * + * @return + * The SEGASTATUS code. + */ +SEGASTATUS SEGAAPI_Init(void); + + +/** + * Exits from the SEGAAPI Library. + * + * This function performs cleanup on the SEGAAPI Library. + * It must be the last function to call. + * + * + * @return + * The SEGASTATUS code. + */ +SEGASTATUS SEGAAPI_Exit(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __SEGAAPI_H */ + diff --git a/src/libsegaapi/segadef.h b/src/libsegaapi/segadef.h new file mode 100644 index 0000000..4a20c92 --- /dev/null +++ b/src/libsegaapi/segadef.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * Copyright (C) 2004 Creative Technology Ltd. All rights reserved. + * + **************************************************************************** + * File: segadef.h + * + * This file contains the types definitions for segaapi. + * + **************************************************************************** + */ + +#ifndef __CTDEF_H + +#ifndef __SEGAAPITYPES_H +#define __SEGAAPITYPES_H + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +/* 8 bit signed value */ +typedef char CTCHAR, *PCTCHAR, **PPCTCHAR; +/* 8 bit unsigned value */ +typedef unsigned char CTBYTE, *PCTBYTE, **PPCTBYTE; + +typedef unsigned char CTUCHAR, *PCTUCHAR, **PPCTUCHAR; + +/* 16 bit signed value */ +typedef short CTSHORT, *PCTSHORT, **PPCTSHORT; +/* 16 bit unsigned value */ +typedef unsigned short CTWORD, *PCTWORD, **PPCTWORD; + +typedef unsigned short CTUSHORT, *PCTUSHORT, **PPCTUSHORT; + +/* 32 bit signed value */ +typedef int CTLONG, *PCTLONG, **PPCTLONG; +/* 32 bit unsigned value */ +typedef unsigned int CTDWORD, *PCTDWORD, **PPCTDWORD; + +typedef unsigned long CTULONG, *PCTULONG, **PPCTULONG; + +typedef int CTBOOL, *PCTBOOL, **PPCTBOOL; + +typedef void * CTHANDLE; + + /* Define basic COM types */ +#ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; +#endif // GUID_DEFINED + +#ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID /*FAR*/ name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID +#endif // DEFINE_GUID + + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif /* __SEGAAPITYPES_H */ + +#endif /* __CTDEF_H */ + + diff --git a/src/libsegaapi/segaeax.h b/src/libsegaapi/segaeax.h new file mode 100644 index 0000000..e7ac40c --- /dev/null +++ b/src/libsegaapi/segaeax.h @@ -0,0 +1,1478 @@ +/** + * Copyright (C) 2005 Creative Technology Ltd. All rights reserved. + * + **************************************************************************** + * \file segaeax.cpp + * \brief + * This file contains the definition of Environmental Audio Extensions version 4.0. + * It is duplicated here for SEGAAPI_SetGlobalEAXProperty and SEGAAPI_GetGlobalEAXProperty + * to use. + * + * @author CW Lim + * + * $Date: 2005/02/02 11:59:54 $ + * + **************************************************************************** + */ + + +#ifndef __SEGAEAX_H +#define __SEGAEAX_H + +#include "segadef.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +#pragma pack(push, 4) + + +//////////////////////////////////////////////////////////////////////////// +// Constants + +#define EAX_MAX_FXSLOTS 4 +#define EAX_MAX_ACTIVE_FXSLOTS 2 + +// The EAX_NULL_GUID is used by EAXFXSLOT_LOADEFFECT, EAXCONTEXT_PRIMARYFXSLOTID +// and EAXSOURCE_ACTIVEFXSLOTID + +// {00000000-0000-0000-0000-000000000000} +DEFINE_GUID(EAX_NULL_GUID, + 0x00000000, + 0x0000, + 0x0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +// The EAX_PrimaryFXSlotID GUID is used by EAXSOURCE_ACTIVEFXSLOTID + +// {F317866D-924C-450C-861B-E6DAA25E7C20} +DEFINE_GUID(EAX_PrimaryFXSlotID, + 0xf317866d, + 0x924c, + 0x450c, + 0x86, 0x1b, 0xe6, 0xda, 0xa2, 0x5e, 0x7c, 0x20); + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Structures + +// Use this structure for EAXCONTEXT_ALL property. +typedef struct _EAXCONTEXTPROPERTIES +{ + GUID guidPrimaryFXSlotID; + float flDistanceFactor; + float flAirAbsorptionHF; + float flHFReference; +} EAXCONTEXTPROPERTIES, *LPEAXCONTEXTPROPERTIES; + +// Use this structure for EAXSOURCE_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +typedef struct _EAXSOURCEPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies EAXREVERB_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXSOURCEPROPERTIES, *LPEAXSOURCEPROPERTIES; + +// Use this structure for EAXSOURCE_ALLSENDPARAMETERS +// - all levels are hundredths of decibels +// +typedef struct _EAXSOURCEALLSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; // send level (at low and mid frequencies) + long lSendHF; // relative send level at high frequencies + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; + long lExclusion; + float flExclusionLFRatio; +} EAXSOURCEALLSENDPROPERTIES, *LPEAXSOURCEALLSENDPROPERTIES; + +// Use this structure for EAXSOURCE_ACTIVEFXSLOTID +typedef struct _EAXACTIVEFXSLOTS +{ + GUID guidActiveFXSlots[EAX_MAX_ACTIVE_FXSLOTS]; +} EAXACTIVEFXSLOTS, *LPEAXACTIVEFXSLOTS; + +// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for EAXSOURCE_OCCLUSIONPARAMETERS property. +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for EAXSOURCE_EXCLUSIONPARAMETERS property. +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Use this structure for EAXSOURCE_SENDPARAMETERS properties. +typedef struct _EAXSOURCESENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; + long lSendHF; +} EAXSOURCESENDPROPERTIES, *LPEAXSOURCESENDPROPERTIES; + +// Use this structure for EAXSOURCE_OCCLUSIONSENDPARAMETERS +typedef struct _EAXSOURCEOCCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXSOURCEOCCLUSIONSENDPROPERTIES, *LPEAXSOURCEOCCLUSIONSENDPROPERTIES; + +// Use this structure for EAXSOURCE_EXCLUSIONSENDPARAMETERS +typedef struct _EAXSOURCEEXCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lExclusion; + float flExclusionLFRatio; +} EAXSOURCEEXCLUSIONSENDPROPERTIES, *LPEAXSOURCEEXCLUSIONSENDPROPERTIES; + +// Use this structure for EAXFXSLOT_ALLPARAMETERS +// - all levels are hundredths of decibels +// +typedef struct _EAXFXSLOTPROPERTIES +{ + GUID guidLoadEffect; + long lVolume; + long lLock; + unsigned long ulFlags; +} EAXFXSLOTPROPERTIES, *LPEAXFXSLOTPROPERTIES; + +// Use this structure for EAXREVERB_REFLECTIONSPAN and EAXREVERB_REVERBPAN properties. +#ifndef EAXVECTOR_DEFINED +#define EAXVECTOR_DEFINED +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; +#endif + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Error Codes + +#define EAX_OK 0 +#define EAXERR_INVALID_OPERATION (-1) +#define EAXERR_INVALID_VALUE (-2) +#define EAXERR_NO_EFFECT_LOADED (-3) +#define EAXERR_UNKNOWN_EFFECT (-4) +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Context Object + +// {1D4870AD-0DEF-43c0-A40C-523632296342} +DEFINE_GUID(EAXPROPERTYID_EAX40_Context, + 0x1d4870ad, + 0xdef, + 0x43c0, + 0xa4, 0xc, 0x52, 0x36, 0x32, 0x29, 0x63, 0x42); + +// For compatibility with future EAX versions: +#define EAXPROPERTYID_EAX_Context EAXPROPERTYID_EAX40_Context + +typedef enum +{ + EAXCONTEXT_NONE = 0, + EAXCONTEXT_ALLPARAMETERS, + EAXCONTEXT_PRIMARYFXSLOTID, + EAXCONTEXT_DISTANCEFACTOR, + EAXCONTEXT_AIRABSORPTIONHF, + EAXCONTEXT_HFREFERENCE, + EAXCONTEXT_LASTERROR +} EAXCONTEXT_PROPERTY; + +// OR these flags with property id +#define EAXCONTEXT_PARAMETER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXCONTEXT_PARAMETER_DEFER 0x80000000 // changes take effect later +#define EAXCONTEXT_PARAMETER_COMMITDEFERREDSETTINGS (EAXCONTEXT_NONE | \ + EAXCONTEXT_PARAMETER_IMMEDIATE) + +// EAX Context property ranges and defaults: +#define EAXCONTEXT_DEFAULTPRIMARYFXSLOTID EAXPROPERTYID_EAX40_FXSlot0 + +#define EAXCONTEXT_MINDISTANCEFACTOR FLT_MIN //minimum positive value +#define EAXCONTEXT_MAXDISTANCEFACTOR FLT_MAX +#define EAXCONTEXT_DEFAULTDISTANCEFACTOR 1.0f + +#define EAXCONTEXT_MINAIRABSORPTIONHF (-100.0f) +#define EAXCONTEXT_MAXAIRABSORPTIONHF 0.0f +#define EAXCONTEXT_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXCONTEXT_MINHFREFERENCE 1000.0f +#define EAXCONTEXT_MAXHFREFERENCE 20000.0f +#define EAXCONTEXT_DEFAULTHFREFERENCE 5000.0f + +#define EAXCONTEXT_DEFAULTLASTERROR EAX_OK + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Effect Slot Objects + +// {C4D79F1E-F1AC-436b-A81D-A738E7045469} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot0, + 0xc4d79f1e, + 0xf1ac, + 0x436b, + 0xa8, 0x1d, 0xa7, 0x38, 0xe7, 0x4, 0x54, 0x69); + +// {08C00E96-74BE-4491-93AA-E8AD35A49117} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot1, + 0x8c00e96, + 0x74be, + 0x4491, + 0x93, 0xaa, 0xe8, 0xad, 0x35, 0xa4, 0x91, 0x17); + +// {1D433B88-F0F6-4637-919F-60E7E06B5EDD} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot2, + 0x1d433b88, + 0xf0f6, + 0x4637, + 0x91, 0x9f, 0x60, 0xe7, 0xe0, 0x6b, 0x5e, 0xdd); + +// {EFFF08EA-C7D8-44ab-93AD-6DBD5F910064} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot3, + 0xefff08ea, + 0xc7d8, + 0x44ab, + 0x93, 0xad, 0x6d, 0xbd, 0x5f, 0x91, 0x0, 0x64); + +// For compatibility with future EAX versions: +#define EAXPROPERTYID_EAX_FXSlot0 EAXPROPERTYID_EAX40_FXSlot0 +#define EAXPROPERTYID_EAX_FXSlot1 EAXPROPERTYID_EAX40_FXSlot1 +#define EAXPROPERTYID_EAX_FXSlot2 EAXPROPERTYID_EAX40_FXSlot2 +#define EAXPROPERTYID_EAX_FXSlot3 EAXPROPERTYID_EAX40_FXSlot3 + +// FXSlot object properties +typedef enum +{ + EAXFXSLOT_PARAMETER = 0, // range 0-0x40 reserved for loaded effect parameters + EAXFXSLOT_NONE = 0x10000, + EAXFXSLOT_ALLPARAMETERS, + EAXFXSLOT_LOADEFFECT, + EAXFXSLOT_VOLUME, + EAXFXSLOT_LOCK, + EAXFXSLOT_FLAGS +} EAXFXSLOT_PROPERTY; + +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXFXSLOTFLAGS_ENVIRONMENT; +// instead of: +// myFlags = 0x00000001; +// +#define EAXFXSLOTFLAGS_ENVIRONMENT 0x00000001 +#define EAXFXSLOTFLAGS_RESERVED 0xFFFFFFFE // reserved future use + +// EAX Effect Slot property ranges and defaults: +#define EAXFXSLOT_MINVOLUME (-10000) +#define EAXFXSLOT_MAXVOLUME 0 +#define EAXFXSLOT_DEFAULTVOLUME 0 + +#define EAXFXSLOT_MINLOCK 0 +#define EAXFXSLOT_MAXLOCK 1 + +enum +{ + EAXFXSLOT_UNLOCKED = 0, + EAXFXSLOT_LOCKED = 1 +}; + +#define EAXFXSLOT_DEFAULTFLAGS (EAXFXSLOTFLAGS_ENVIRONMENT) +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Source Object + +// {1B86B823-22DF-4eae-8B3C-1278CE544227} +DEFINE_GUID(EAXPROPERTYID_EAX40_Source, + 0x1b86b823, + 0x22df, + 0x4eae, + 0x8b, 0x3c, 0x12, 0x78, 0xce, 0x54, 0x42, 0x27); + +// For compatibility with future EAX versions: +#define EAXPROPERTYID_EAX_Source EAXPROPERTYID_EAX40_Source + +// Source object properties +typedef enum +{ + EAXSOURCE_NONE, + EAXSOURCE_ALLPARAMETERS, + EAXSOURCE_OBSTRUCTIONPARAMETERS, + EAXSOURCE_OCCLUSIONPARAMETERS, + EAXSOURCE_EXCLUSIONPARAMETERS, + EAXSOURCE_DIRECT, + EAXSOURCE_DIRECTHF, + EAXSOURCE_ROOM, + EAXSOURCE_ROOMHF, + EAXSOURCE_OBSTRUCTION, + EAXSOURCE_OBSTRUCTIONLFRATIO, + EAXSOURCE_OCCLUSION, + EAXSOURCE_OCCLUSIONLFRATIO, + EAXSOURCE_OCCLUSIONROOMRATIO, + EAXSOURCE_OCCLUSIONDIRECTRATIO, + EAXSOURCE_EXCLUSION, + EAXSOURCE_EXCLUSIONLFRATIO, + EAXSOURCE_OUTSIDEVOLUMEHF, + EAXSOURCE_DOPPLERFACTOR, + EAXSOURCE_ROLLOFFFACTOR, + EAXSOURCE_ROOMROLLOFFFACTOR, + EAXSOURCE_AIRABSORPTIONFACTOR, + EAXSOURCE_FLAGS, + EAXSOURCE_SENDPARAMETERS, + EAXSOURCE_ALLSENDPARAMETERS, + EAXSOURCE_OCCLUSIONSENDPARAMETERS, + EAXSOURCE_EXCLUSIONSENDPARAMETERS, + EAXSOURCE_ACTIVEFXSLOTID, +} EAXSOURCE_PROPERTY; + +// OR these flags with property id +#define EAXSOURCE_PARAMETER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXSOURCE_PARAMETER_DEFERRED 0x80000000 // changes take effect later +#define EAXSOURCE_PARAMETER_COMMITDEFERREDSETTINGS (EAXSOURCE_NONE | \ + EAXSOURCE_PARAMETER_IMMEDIATE) +// Used by EAXSOURCE_FLAGS for EAXSOURCEFLAGS_xxxAUTO +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXSOURCE_DIRECTHFAUTO | EAXSOURCE_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXSOURCEFLAGS_DIRECTHFAUTO 0x00000001 // relates to EAXSOURCE_DIRECTHF +#define EAXSOURCEFLAGS_ROOMAUTO 0x00000002 // relates to EAXSOURCE_ROOM +#define EAXSOURCEFLAGS_ROOMHFAUTO 0x00000004 // relates to EAXSOURCE_ROOMHF +#define EAXSOURCEFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// EAX Source property ranges and defaults: +#define EAXSOURCE_MINSEND (-10000) +#define EAXSOURCE_MAXSEND 0 +#define EAXSOURCE_DEFAULTSEND 0 + +#define EAXSOURCE_MINSENDHF (-10000) +#define EAXSOURCE_MAXSENDHF 0 +#define EAXSOURCE_DEFAULTSENDHF 0 + +#define EAXSOURCE_MINDIRECT (-10000) +#define EAXSOURCE_MAXDIRECT 1000 +#define EAXSOURCE_DEFAULTDIRECT 0 + +#define EAXSOURCE_MINDIRECTHF (-10000) +#define EAXSOURCE_MAXDIRECTHF 0 +#define EAXSOURCE_DEFAULTDIRECTHF 0 + +#define EAXSOURCE_MINROOM (-10000) +#define EAXSOURCE_MAXROOM 1000 +#define EAXSOURCE_DEFAULTROOM 0 + +#define EAXSOURCE_MINROOMHF (-10000) +#define EAXSOURCE_MAXROOMHF 0 +#define EAXSOURCE_DEFAULTROOMHF 0 + +#define EAXSOURCE_MINOBSTRUCTION (-10000) +#define EAXSOURCE_MAXOBSTRUCTION 0 +#define EAXSOURCE_DEFAULTOBSTRUCTION 0 + +#define EAXSOURCE_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXSOURCE_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXSOURCE_MINOCCLUSION (-10000) +#define EAXSOURCE_MAXOCCLUSION 0 +#define EAXSOURCE_DEFAULTOCCLUSION 0 + +#define EAXSOURCE_MINOCCLUSIONLFRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXSOURCE_MINOCCLUSIONROOMRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXSOURCE_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXSOURCE_MINEXCLUSION (-10000) +#define EAXSOURCE_MAXEXCLUSION 0 +#define EAXSOURCE_DEFAULTEXCLUSION 0 + +#define EAXSOURCE_MINEXCLUSIONLFRATIO 0.0f +#define EAXSOURCE_MAXEXCLUSIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXSOURCE_MINOUTSIDEVOLUMEHF (-10000) +#define EAXSOURCE_MAXOUTSIDEVOLUMEHF 0 +#define EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXSOURCE_MINDOPPLERFACTOR 0.0f +#define EAXSOURCE_MAXDOPPLERFACTOR 10.f +#define EAXSOURCE_DEFAULTDOPPLERFACTOR 1.0f + +#define EAXSOURCE_MINROLLOFFFACTOR 0.0f +#define EAXSOURCE_MAXROLLOFFFACTOR 10.f +#define EAXSOURCE_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXSOURCE_MINROOMROLLOFFFACTOR 0.0f +#define EAXSOURCE_MAXROOMROLLOFFFACTOR 10.f +#define EAXSOURCE_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXSOURCE_MINAIRABSORPTIONFACTOR 0.0f +#define EAXSOURCE_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR 0.0f + +#define EAXSOURCE_DEFAULTFLAGS (EAXSOURCEFLAGS_DIRECTHFAUTO | \ + EAXSOURCEFLAGS_ROOMAUTO | \ + EAXSOURCEFLAGS_ROOMHFAUTO ) + +#define EAXSOURCE_DEFAULTACTIVEFXSLOTID {{ EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] }, \ + { EAX_PrimaryFXSlotID.Data1, EAX_PrimaryFXSlotID.Data2, \ + EAX_PrimaryFXSlotID.Data3, EAX_PrimaryFXSlotID.Data4[0],\ + EAX_PrimaryFXSlotID.Data4[1],EAX_PrimaryFXSlotID.Data4[2],\ + EAX_PrimaryFXSlotID.Data4[3],EAX_PrimaryFXSlotID.Data4[4],\ + EAX_PrimaryFXSlotID.Data4[5],EAX_PrimaryFXSlotID.Data4[6],\ + EAX_PrimaryFXSlotID.Data4[7] }} + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Reverb Effect + +// EAX REVERB {0CF95C8F-A3CC-4849-B0B6-832ECC1822DF} +DEFINE_GUID(EAX_REVERB_EFFECT, + 0xcf95c8f, + 0xa3cc, + 0x4849, + 0xb0, 0xb6, 0x83, 0x2e, 0xcc, 0x18, 0x22, 0xdf); + +// Reverb effect properties +typedef enum +{ + EAXREVERB_NONE, + EAXREVERB_ALLPARAMETERS, + EAXREVERB_ENVIRONMENT, + EAXREVERB_ENVIRONMENTSIZE, + EAXREVERB_ENVIRONMENTDIFFUSION, + EAXREVERB_ROOM, + EAXREVERB_ROOMHF, + EAXREVERB_ROOMLF, + EAXREVERB_DECAYTIME, + EAXREVERB_DECAYHFRATIO, + EAXREVERB_DECAYLFRATIO, + EAXREVERB_REFLECTIONS, + EAXREVERB_REFLECTIONSDELAY, + EAXREVERB_REFLECTIONSPAN, + EAXREVERB_REVERB, + EAXREVERB_REVERBDELAY, + EAXREVERB_REVERBPAN, + EAXREVERB_ECHOTIME, + EAXREVERB_ECHODEPTH, + EAXREVERB_MODULATIONTIME, + EAXREVERB_MODULATIONDEPTH, + EAXREVERB_AIRABSORPTIONHF, + EAXREVERB_HFREFERENCE, + EAXREVERB_LFREFERENCE, + EAXREVERB_ROOMROLLOFFFACTOR, + EAXREVERB_FLAGS, +} EAXREVERB_PROPERTY; + +// OR these flags with property id +#define EAXREVERB_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXREVERB_DEFERRED 0x80000000 // changes take effect later +#define EAXREVERB_COMMITDEFERREDSETTINGS (EAXREVERB_NONE | \ + EAXREVERB_IMMEDIATE) + +// used by EAXREVERB_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by EAXREVERB_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXREVERBFLAGS_DECAYTIMESCALE | EAXREVERBFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXREVERBFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXREVERBFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXREVERBFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXREVERBFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXREVERBFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXREVERBFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXREVERBFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time +// This flag limits high-frequency decay time according to air absorption. +#define EAXREVERBFLAGS_DECAYHFLIMIT 0x00000020 +#define EAXREVERBFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Use this structure for EAXREVERB_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +typedef struct _EAXREVERBPROPERTIES +{ + unsigned long ulEnvironment; // sets all reverb properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXREVERBPROPERTIES, *LPEAXREVERBPROPERTIES; + +// Property ranges and defaults: +#define EAXREVERB_MINENVIRONMENT 0 +#define EAXREVERB_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXREVERB_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXREVERB_MINENVIRONMENTSIZE 1.0f +#define EAXREVERB_MAXENVIRONMENTSIZE 100.0f +#define EAXREVERB_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXREVERB_MINENVIRONMENTDIFFUSION 0.0f +#define EAXREVERB_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXREVERB_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXREVERB_MINROOM (-10000) +#define EAXREVERB_MAXROOM 0 +#define EAXREVERB_DEFAULTROOM (-1000) + +#define EAXREVERB_MINROOMHF (-10000) +#define EAXREVERB_MAXROOMHF 0 +#define EAXREVERB_DEFAULTROOMHF (-100) + +#define EAXREVERB_MINROOMLF (-10000) +#define EAXREVERB_MAXROOMLF 0 +#define EAXREVERB_DEFAULTROOMLF 0 + +#define EAXREVERB_MINDECAYTIME 0.1f +#define EAXREVERB_MAXDECAYTIME 20.0f +#define EAXREVERB_DEFAULTDECAYTIME 1.49f + +#define EAXREVERB_MINDECAYHFRATIO 0.1f +#define EAXREVERB_MAXDECAYHFRATIO 2.0f +#define EAXREVERB_DEFAULTDECAYHFRATIO 0.83f + +#define EAXREVERB_MINDECAYLFRATIO 0.1f +#define EAXREVERB_MAXDECAYLFRATIO 2.0f +#define EAXREVERB_DEFAULTDECAYLFRATIO 1.00f + +#define EAXREVERB_MINREFLECTIONS (-10000) +#define EAXREVERB_MAXREFLECTIONS 1000 +#define EAXREVERB_DEFAULTREFLECTIONS (-2602) + +#define EAXREVERB_MINREFLECTIONSDELAY 0.0f +#define EAXREVERB_MAXREFLECTIONSDELAY 0.3f +#define EAXREVERB_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXREVERB_DEFAULTREFLECTIONSPAN {0.0f, 0.0f, 0.0f} + +#define EAXREVERB_MINREVERB (-10000) +#define EAXREVERB_MAXREVERB 2000 +#define EAXREVERB_DEFAULTREVERB 200 + +#define EAXREVERB_MINREVERBDELAY 0.0f +#define EAXREVERB_MAXREVERBDELAY 0.1f +#define EAXREVERB_DEFAULTREVERBDELAY 0.011f + +#define EAXREVERB_DEFAULTREVERBPAN {0.0f, 0.0f, 0.0f} + +#define EAXREVERB_MINECHOTIME 0.075f +#define EAXREVERB_MAXECHOTIME 0.25f +#define EAXREVERB_DEFAULTECHOTIME 0.25f + +#define EAXREVERB_MINECHODEPTH 0.0f +#define EAXREVERB_MAXECHODEPTH 1.0f +#define EAXREVERB_DEFAULTECHODEPTH 0.0f + +#define EAXREVERB_MINMODULATIONTIME 0.04f +#define EAXREVERB_MAXMODULATIONTIME 4.0f +#define EAXREVERB_DEFAULTMODULATIONTIME 0.25f + +#define EAXREVERB_MINMODULATIONDEPTH 0.0f +#define EAXREVERB_MAXMODULATIONDEPTH 1.0f +#define EAXREVERB_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXREVERB_MINAIRABSORPTIONHF (-100.0f) +#define EAXREVERB_MAXAIRABSORPTIONHF 0.0f +#define EAXREVERB_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXREVERB_MINHFREFERENCE 1000.0f +#define EAXREVERB_MAXHFREFERENCE 20000.0f +#define EAXREVERB_DEFAULTHFREFERENCE 5000.0f + +#define EAXREVERB_MINLFREFERENCE 20.0f +#define EAXREVERB_MAXLFREFERENCE 1000.0f +#define EAXREVERB_DEFAULTLFREFERENCE 250.0f + +#define EAXREVERB_MINROOMROLLOFFFACTOR 0.0f +#define EAXREVERB_MAXROOMROLLOFFFACTOR 10.0f +#define EAXREVERB_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXREVERB_DEFAULTFLAGS (EAXREVERBFLAGS_DECAYTIMESCALE | \ + EAXREVERBFLAGS_REFLECTIONSSCALE | \ + EAXREVERBFLAGS_REFLECTIONSDELAYSCALE | \ + EAXREVERBFLAGS_REVERBSCALE | \ + EAXREVERBFLAGS_REVERBDELAYSCALE | \ + EAXREVERBFLAGS_DECAYHFLIMIT) +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// + +// New Effect Types + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// AGC Compressor Effect + +// EAX AGC COMPRESSOR {BFB7A01E-7825-4039-927F-3AABDA0C560} + +DEFINE_GUID(EAX_AGCCOMPRESSOR_EFFECT, + 0xbfb7a01e, + 0x7825, + 0x4039, + 0x92, 0x7f, 0x3, 0xaa, 0xbd, 0xa0, 0xc5, 0x60); + +// AGC Compressor properties +typedef enum +{ + EAXAGCCOMPRESSOR_NONE, + EAXAGCCOMPRESSOR_ALLPARAMETERS, + EAXAGCCOMPRESSOR_ONOFF +} EAXAGCCOMPRESSOR_PROPERTY; + +// OR these flags with property id +#define EAXAGCCOMPRESSOR_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXAGCCOMPRESSOR_DEFERRED 0x80000000 // changes take effect later +#define EAXAGCCOMPRESSOR_COMMITDEFERREDSETTINGS (EAXAGCCOMPRESSOR_NONE | \ + EAXAGCCOMPRESSOR_IMMEDIATE) + +// Use this structure for EAXAGCCOMPRESSOR_ALLPARAMETERS +typedef struct _EAXAGCCOMPRESSORPROPERTIES +{ + unsigned long ulOnOff; // Switch Compressor on or off +} EAXAGCCOMPRESSORPROPERTIES, *LPEAXAGCCOMPRESSORPROPERTIES; + +// Property ranges and defaults: + +#define EAXAGCCOMPRESSOR_MINONOFF 0 +#define EAXAGCCOMPRESSOR_MAXONOFF 1 +#define EAXAGCCOMPRESSOR_DEFAULTONOFF 1 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Autowah Effect + +// EAX AUTOWAH {EC3130C0-AC7A-11D2-88DD-A024D13CE1} +DEFINE_GUID(EAX_AUTOWAH_EFFECT, + 0xec3130c0, + 0xac7a, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Autowah properties +typedef enum +{ + EAXAUTOWAH_NONE, + EAXAUTOWAH_ALLPARAMETERS, + EAXAUTOWAH_ATTACKTIME, + EAXAUTOWAH_RELEASETIME, + EAXAUTOWAH_RESONANCE, + EAXAUTOWAH_PEAKLEVEL +} EAXAUTOWAH_PROPERTY; + +// OR these flags with property id +#define EAXAUTOWAH_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXAUTOWAH_DEFERRED 0x80000000 // changes take effect later +#define EAXAUTOWAH_COMMITDEFERREDSETTINGS (EAXAUTOWAH_NONE | \ + EAXAUTOWAH_IMMEDIATE) + +// Use this structure for EAXAUTOWAH_ALLPARAMETERS +typedef struct _EAXAUTOWAHPROPERTIES +{ + float flAttackTime; // Attack time (seconds) + float flReleaseTime; // Release time (seconds) + long lResonance; // Resonance (mB) + long lPeakLevel; // Peak level (mB) +} EAXAUTOWAHPROPERTIES, *LPEAXAUTOWAHPROPERTIES; + +// Property ranges and defaults: + +#define EAXAUTOWAH_MINATTACKTIME 0.0001f +#define EAXAUTOWAH_MAXATTACKTIME 1.0f +#define EAXAUTOWAH_DEFAULTATTACKTIME 0.06f + +#define EAXAUTOWAH_MINRELEASETIME 0.0001f +#define EAXAUTOWAH_MAXRELEASETIME 1.0f +#define EAXAUTOWAH_DEFAULTRELEASETIME 0.06f + +#define EAXAUTOWAH_MINRESONANCE 600 +#define EAXAUTOWAH_MAXRESONANCE 6000 +#define EAXAUTOWAH_DEFAULTRESONANCE 6000 + +#define EAXAUTOWAH_MINPEAKLEVEL (-9000) +#define EAXAUTOWAH_MAXPEAKLEVEL 9000 +#define EAXAUTOWAH_DEFAULTPEAKLEVEL 2100 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Chorus Effect + +// EAX CHORUS {DE6D6FE0-AC79-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_CHORUS_EFFECT, + 0xde6d6fe0, + 0xac79, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + + +// Chorus properties +typedef enum +{ + EAXCHORUS_NONE, + EAXCHORUS_ALLPARAMETERS, + EAXCHORUS_WAVEFORM, + EAXCHORUS_PHASE, + EAXCHORUS_RATE, + EAXCHORUS_DEPTH, + EAXCHORUS_FEEDBACK, + EAXCHORUS_DELAY +} EAXCHORUS_PROPERTY; + +// OR these flags with property id +#define EAXCHORUS_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXCHORUS_DEFERRED 0x80000000 // changes take effect later +#define EAXCHORUS_COMMITDEFERREDSETTINGS (EAXCHORUS_NONE | \ + EAXCHORUS_IMMEDIATE) + +// used by EAXCHORUS_WAVEFORM +enum +{ + EAX_CHORUS_SINUSOID, + EAX_CHORUS_TRIANGLE +}; + +// Use this structure for EAXCHORUS_ALLPARAMETERS +typedef struct _EAXCHORUSPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (-1 to 1) + float flDelay; // Delay (seconds) +} EAXCHORUSPROPERTIES, *LPEAXCHORUSPROPERTIES; + +// Property ranges and defaults: + +#define EAXCHORUS_MINWAVEFORM 0 +#define EAXCHORUS_MAXWAVEFORM 1 +#define EAXCHORUS_DEFAULTWAVEFORM 1 + +#define EAXCHORUS_MINPHASE (-180) +#define EAXCHORUS_MAXPHASE 180 +#define EAXCHORUS_DEFAULTPHASE 90 + +#define EAXCHORUS_MINRATE 0.0f +#define EAXCHORUS_MAXRATE 10.0f +#define EAXCHORUS_DEFAULTRATE 1.1f + +#define EAXCHORUS_MINDEPTH 0.0f +#define EAXCHORUS_MAXDEPTH 1.0f +#define EAXCHORUS_DEFAULTDEPTH 0.1f + +#define EAXCHORUS_MINFEEDBACK (-1.0f) +#define EAXCHORUS_MAXFEEDBACK 1.0f +#define EAXCHORUS_DEFAULTFEEDBACK 0.25f + +#define EAXCHORUS_MINDELAY 0.0002f +#define EAXCHORUS_MAXDELAY 0.016f +#define EAXCHORUS_DEFAULTDELAY 0.016f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Distortion Effect + +// EAX DISTORTION {975A4CE0-AC7E-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_DISTORTION_EFFECT, + 0x975a4ce0, + 0xac7e, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Distortion properties +typedef enum +{ + EAXDISTORTION_NONE, + EAXDISTORTION_ALLPARAMETERS, + EAXDISTORTION_EDGE, + EAXDISTORTION_GAIN, + EAXDISTORTION_LOWPASSCUTOFF, + EAXDISTORTION_EQCENTER, + EAXDISTORTION_EQBANDWIDTH +} EAXDISTORTION_PROPERTY; + +// OR these flags with property id +#define EAXDISTORTION_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXDISTORTION_DEFERRED 0x80000000 // changes take effect later +#define EAXDISTORTION_COMMITDEFERREDSETTINGS (EAXDISTORTION_NONE | \ + EAXDISTORTION_IMMEDIATE) + +// Use this structure for EAXDISTORTION_ALLPARAMETERS +typedef struct _EAXDISTORTIONPROPERTIES +{ + float flEdge; // Controls the shape of the distortion (0 to 1) + long lGain; // Controls the post distortion gain (mB) + float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz) + float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz) + float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz) +} EAXDISTORTIONPROPERTIES, *LPEAXDISTORTIONPROPERTIES; + +// Property ranges and defaults: + +#define EAXDISTORTION_MINEDGE 0.0f +#define EAXDISTORTION_MAXEDGE 1.0f +#define EAXDISTORTION_DEFAULTEDGE 0.2f + +#define EAXDISTORTION_MINGAIN (-6000) +#define EAXDISTORTION_MAXGAIN 0 +#define EAXDISTORTION_DEFAULTGAIN (-2600) + +#define EAXDISTORTION_MINLOWPASSCUTOFF 80.0f +#define EAXDISTORTION_MAXLOWPASSCUTOFF 24000.0f +#define EAXDISTORTION_DEFAULTLOWPASSCUTOFF 8000.0f + +#define EAXDISTORTION_MINEQCENTER 80.0f +#define EAXDISTORTION_MAXEQCENTER 24000.0f +#define EAXDISTORTION_DEFAULTEQCENTER 3600.0f + +#define EAXDISTORTION_MINEQBANDWIDTH 80.0f +#define EAXDISTORTION_MAXEQBANDWIDTH 24000.0f +#define EAXDISTORTION_DEFAULTEQBANDWIDTH 3600.0f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Echo Effect + +// EAX ECHO {E9F1BC0-AC82-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_ECHO_EFFECT, + 0xe9f1bc0, + 0xac82, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Echo properties +typedef enum +{ + EAXECHO_NONE, + EAXECHO_ALLPARAMETERS, + EAXECHO_DELAY, + EAXECHO_LRDELAY, + EAXECHO_DAMPING, + EAXECHO_FEEDBACK, + EAXECHO_SPREAD +} EAXECHO_PROPERTY; + +// OR these flags with property id +#define EAXECHO_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXECHO_DEFERRED 0x80000000 // changes take effect later +#define EAXECHO_COMMITDEFERREDSETTINGS (EAXECHO_NONE | \ + EAXECHO_IMMEDIATE) + +// Use this structure for EAXECHO_ALLPARAMETERS +typedef struct _EAXECHOPROPERTIES +{ + float flDelay; // Controls the initial delay time (seconds) + float flLRDelay; // Controls the delay time between the first and second taps (seconds) + float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1) + float flFeedback; // Controls the duration of echo repetition (0 to 1) + float flSpread; // Controls the left-right spread of the echoes +} EAXECHOPROPERTIES, *LPEAXECHOPROPERTIES; + +// Property ranges and defaults: + +#define EAXECHO_MINDAMPING 0.0f +#define EAXECHO_MAXDAMPING 0.99f +#define EAXECHO_DEFAULTDAMPING 0.5f + +#define EAXECHO_MINDELAY 0.002f +#define EAXECHO_MAXDELAY 0.207f +#define EAXECHO_DEFAULTDELAY 0.1f + +#define EAXECHO_MINLRDELAY 0.0f +#define EAXECHO_MAXLRDELAY 0.404f +#define EAXECHO_DEFAULTLRDELAY 0.1f + +#define EAXECHO_MINFEEDBACK 0.0f +#define EAXECHO_MAXFEEDBACK 1.0f +#define EAXECHO_DEFAULTFEEDBACK 0.5f + +#define EAXECHO_MINSPREAD (-1.0f) +#define EAXECHO_MAXSPREAD 1.0f +#define EAXECHO_DEFAULTSPREAD (-1.0f) + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Equalizer Effect + +// EAX EQUALIZER {65F94CE0-9793-11D3-939D-C0F02DD6F0} + +DEFINE_GUID(EAX_EQUALIZER_EFFECT, + 0x65f94ce0, + 0x9793, + 0x11d3, + 0x93, 0x9d, 0x0, 0xc0, 0xf0, 0x2d, 0xd6, 0xf0); + + +// Equalizer properties +typedef enum +{ + EAXEQUALIZER_NONE, + EAXEQUALIZER_ALLPARAMETERS, + EAXEQUALIZER_LOWGAIN, + EAXEQUALIZER_LOWCUTOFF, + EAXEQUALIZER_MID1GAIN, + EAXEQUALIZER_MID1CENTER, + EAXEQUALIZER_MID1WIDTH, + EAXEQUALIZER_MID2GAIN, + EAXEQUALIZER_MID2CENTER, + EAXEQUALIZER_MID2WIDTH, + EAXEQUALIZER_HIGHGAIN, + EAXEQUALIZER_HIGHCUTOFF +} EAXEQUALIZER_PROPERTY; + +// OR these flags with property id +#define EAXEQUALIZER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXEQUALIZER_DEFERRED 0x80000000 // changes take effect later +#define EAXEQUALIZER_COMMITDEFERREDSETTINGS (EAXEQUALIZER_NONE | \ + EAXEQUALIZER_IMMEDIATE) + +// Use this structure for EAXEQUALIZER_ALLPARAMETERS +typedef struct _EAXEQUALIZERPROPERTIES +{ + long lLowGain; // (mB) + float flLowCutOff; // (Hz) + long lMid1Gain; // (mB) + float flMid1Center; // (Hz) + float flMid1Width; // (octaves) + long lMid2Gain; // (mB) + float flMid2Center; // (Hz) + float flMid2Width; // (octaves) + long lHighGain; // (mB) + float flHighCutOff; // (Hz) +} EAXEQUALIZERPROPERTIES, *LPEAXEQUALIZERPROPERTIES; + +// Property ranges and defaults: + +#define EAXEQUALIZER_MINLOWGAIN (-1800) +#define EAXEQUALIZER_MAXLOWGAIN 1800 +#define EAXEQUALIZER_DEFAULTLOWGAIN 0 + +#define EAXEQUALIZER_MINLOWCUTOFF 50.0f +#define EAXEQUALIZER_MAXLOWCUTOFF 800.0f +#define EAXEQUALIZER_DEFAULTLOWCUTOFF 200.0f + +#define EAXEQUALIZER_MINMID1GAIN (-1800) +#define EAXEQUALIZER_MAXMID1GAIN 1800 +#define EAXEQUALIZER_DEFAULTMID1GAIN 0 + +#define EAXEQUALIZER_MINMID1CENTER 200.0f +#define EAXEQUALIZER_MAXMID1CENTER 3000.0f +#define EAXEQUALIZER_DEFAULTMID1CENTER 500.0f + +#define EAXEQUALIZER_MINMID1WIDTH 0.01f +#define EAXEQUALIZER_MAXMID1WIDTH 1.0f +#define EAXEQUALIZER_DEFAULTMID1WIDTH 1.0f + +#define EAXEQUALIZER_MINMID2GAIN (-1800) +#define EAXEQUALIZER_MAXMID2GAIN 1800 +#define EAXEQUALIZER_DEFAULTMID2GAIN 0 + +#define EAXEQUALIZER_MINMID2CENTER 1000.0f +#define EAXEQUALIZER_MAXMID2CENTER 8000.0f +#define EAXEQUALIZER_DEFAULTMID2CENTER 3000.0f + +#define EAXEQUALIZER_MINMID2WIDTH 0.01f +#define EAXEQUALIZER_MAXMID2WIDTH 1.0f +#define EAXEQUALIZER_DEFAULTMID2WIDTH 1.0f + +#define EAXEQUALIZER_MINHIGHGAIN (-1800) +#define EAXEQUALIZER_MAXHIGHGAIN 1800 +#define EAXEQUALIZER_DEFAULTHIGHGAIN 0 + +#define EAXEQUALIZER_MINHIGHCUTOFF 4000.0f +#define EAXEQUALIZER_MAXHIGHCUTOFF 16000.0f +#define EAXEQUALIZER_DEFAULTHIGHCUTOFF 6000.0f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Flanger Effect + +// EAX FLANGER {A70007C0-7D2-11D3-9B1E-A024D13CE1} + +DEFINE_GUID(EAX_FLANGER_EFFECT, + 0xa70007c0, + 0x7d2, + 0x11d3, + 0x9b, 0x1e, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Flanger properties +typedef enum +{ + EAXFLANGER_NONE, + EAXFLANGER_ALLPARAMETERS, + EAXFLANGER_WAVEFORM, + EAXFLANGER_PHASE, + EAXFLANGER_RATE, + EAXFLANGER_DEPTH, + EAXFLANGER_FEEDBACK, + EAXFLANGER_DELAY +} EAXFLANGER_PROPERTY; + +// OR these flags with property id +#define EAXFLANGER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXFLANGER_DEFERRED 0x80000000 // changes take effect later +#define EAXFLANGER_COMMITDEFERREDSETTINGS (EAXFLANGER_NONE | \ + EAXFLANGER_IMMEDIATE) + +// used by EAXFLANGER_WAVEFORM +enum +{ + EAX_FLANGER_SINUSOID, + EAX_FLANGER_TRIANGLE +}; + +// Use this structure for EAXFLANGER_ALLPARAMETERS +typedef struct _EAXFLANGERPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (0 to 1) + float flDelay; // Delay (seconds) +} EAXFLANGERPROPERTIES, *LPEAXFLANGERPROPERTIES; + +// Property ranges and defaults: + +#define EAXFLANGER_MINWAVEFORM 0 +#define EAXFLANGER_MAXWAVEFORM 1 +#define EAXFLANGER_DEFAULTWAVEFORM 1 + +#define EAXFLANGER_MINPHASE (-180) +#define EAXFLANGER_MAXPHASE 180 +#define EAXFLANGER_DEFAULTPHASE 0 + +#define EAXFLANGER_MINRATE 0.0f +#define EAXFLANGER_MAXRATE 10.0f +#define EAXFLANGER_DEFAULTRATE 0.27f + +#define EAXFLANGER_MINDEPTH 0.0f +#define EAXFLANGER_MAXDEPTH 1.0f +#define EAXFLANGER_DEFAULTDEPTH 1.0f + +#define EAXFLANGER_MINFEEDBACK (-1.0f) +#define EAXFLANGER_MAXFEEDBACK 1.0f +#define EAXFLANGER_DEFAULTFEEDBACK (-0.5f) + +#define EAXFLANGER_MINDELAY 0.0002f +#define EAXFLANGER_MAXDELAY 0.004f +#define EAXFLANGER_DEFAULTDELAY 0.002f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Frequency Shifter Effect + +// EAX FREQUENCY SHIFTER {DC3E1880-9212-11D3-939D-C0F02DD6F0} + +DEFINE_GUID(EAX_FREQUENCYSHIFTER_EFFECT, + 0xdc3e1880, + 0x9212, + 0x11d3, + 0x93, 0x9d, 0x0, 0xc0, 0xf0, 0x2d, 0xd6, 0xf0); + +// Frequency Shifter properties +typedef enum +{ + EAXFREQUENCYSHIFTER_NONE, + EAXFREQUENCYSHIFTER_ALLPARAMETERS, + EAXFREQUENCYSHIFTER_FREQUENCY, + EAXFREQUENCYSHIFTER_LEFTDIRECTION, + EAXFREQUENCYSHIFTER_RIGHTDIRECTION +} EAXFREQUENCYSHIFTER_PROPERTY; + +// OR these flags with property id +#define EAXFREQUENCYSHIFTER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXFREQUENCYSHIFTER_DEFERRED 0x80000000 // changes take effect later +#define EAXFREQUENCYSHIFTER_COMMITDEFERREDSETTINGS (EAXFREQUENCYSHIFTER_NONE | \ + EAXFREQUENCYSHIFTER_IMMEDIATE) + +// used by EAXFREQUENCYSHIFTER_LEFTDIRECTION and EAXFREQUENCYSHIFTER_RIGHTDIRECTION +enum +{ + EAX_FREQUENCYSHIFTER_DOWN, + EAX_FREQUENCYSHIFTER_UP, + EAX_FREQUENCYSHIFTER_OFF +}; + +// Use this structure for EAXFREQUENCYSHIFTER_ALLPARAMETERS +typedef struct _EAXFREQUENCYSHIFTERPROPERTIES +{ + float flFrequency; // (Hz) + unsigned long ulLeftDirection; // see enum above + unsigned long ulRightDirection; // see enum above +} EAXFREQUENCYSHIFTERPROPERTIES, *LPEAXFREQUENCYSHIFTERPROPERTIES; + +// Property ranges and defaults: + +#define EAXFREQUENCYSHIFTER_MINFREQUENCY 0.0f +#define EAXFREQUENCYSHIFTER_MAXFREQUENCY 24000.0f +#define EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY 0.0f + +#define EAXFREQUENCYSHIFTER_MINLEFTDIRECTION 0 +#define EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION 2 +#define EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION 0 + +#define EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION 0 +#define EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION 2 +#define EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION 0 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Vocal Morpher Effect + +// EAX VOCAL MORPHER {E41CF10C-3383-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_VOCALMORPHER_EFFECT, + 0xe41cf10c, + 0x3383, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Vocal Morpher properties +typedef enum +{ + EAXVOCALMORPHER_NONE, + EAXVOCALMORPHER_ALLPARAMETERS, + EAXVOCALMORPHER_PHONEMEA, + EAXVOCALMORPHER_PHONEMEACOARSETUNING, + EAXVOCALMORPHER_PHONEMEB, + EAXVOCALMORPHER_PHONEMEBCOARSETUNING, + EAXVOCALMORPHER_WAVEFORM, + EAXVOCALMORPHER_RATE +} EAXVOCALMORPHER_PROPERTY; + +// OR these flags with property id +#define EAXVOCALMORPHER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXVOCALMORPHER_DEFERRED 0x80000000 // changes take effect later +#define EAXVOCALMORPHER_COMMITDEFERREDSETTINGS (EAXVOCALMORPHER_NONE | \ + EAXVOCALMORPHER_IMMEDIATE) + +// used by EAXVOCALMORPHER_PHONEMEA and EAXVOCALMORPHER_PHONEMEB +enum +{ + A, E, I, O, U, AA, AE, AH, AO, EH, ER, IH, IY, UH, UW, B, D, F, G, + J, K, L, M, N, P, R, S, T, V, Z +}; + +// used by EAXVOCALMORPHER_WAVEFORM +enum +{ + EAX_VOCALMORPHER_SINUSOID, + EAX_VOCALMORPHER_TRIANGLE, + EAX_VOCALMORPHER_SAWTOOTH +}; + +// Use this structure for EAXVOCALMORPHER_ALLPARAMETERS +typedef struct _EAXVOCALMORPHERPROPERTIES +{ + unsigned long ulPhonemeA; // see enum above + long lPhonemeACoarseTuning; // (semitones) + unsigned long ulPhonemeB; // see enum above + long lPhonemeBCoarseTuning; // (semitones) + unsigned long ulWaveform; // Waveform selector - see enum above + float flRate; // (Hz) +} EAXVOCALMORPHERPROPERTIES, *LPEAXVOCALMORPHERPROPERTIES; + +// Property ranges and defaults: + +#define EAXVOCALMORPHER_MINPHONEMEA 0 +#define EAXVOCALMORPHER_MAXPHONEMEA 29 +#define EAXVOCALMORPHER_DEFAULTPHONEMEA 0 + +#define EAXVOCALMORPHER_MINPHONEMEACOARSETUNING (-24) +#define EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING 24 +#define EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING 0 + +#define EAXVOCALMORPHER_MINPHONEMEB 0 +#define EAXVOCALMORPHER_MAXPHONEMEB 29 +#define EAXVOCALMORPHER_DEFAULTPHONEMEB 10 + +#define EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING (-24) +#define EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING 24 +#define EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING 0 + +#define EAXVOCALMORPHER_MINWAVEFORM 0 +#define EAXVOCALMORPHER_MAXWAVEFORM 2 +#define EAXVOCALMORPHER_DEFAULTWAVEFORM 0 + +#define EAXVOCALMORPHER_MINRATE 0.0f +#define EAXVOCALMORPHER_MAXRATE 10.0f +#define EAXVOCALMORPHER_DEFAULTRATE 1.41f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Pitch Shifter Effect + +// EAX PITCH SHIFTER {E7905100-AFB2-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_PITCHSHIFTER_EFFECT, + 0xe7905100, + 0xafb2, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Pitch Shifter properties +typedef enum +{ + EAXPITCHSHIFTER_NONE, + EAXPITCHSHIFTER_ALLPARAMETERS, + EAXPITCHSHIFTER_COARSETUNE, + EAXPITCHSHIFTER_FINETUNE +} EAXPITCHSHIFTER_PROPERTY; + +// OR these flags with property id +#define EAXPITCHSHIFTER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXPITCHSHIFTER_DEFERRED 0x80000000 // changes take effect later +#define EAXPITCHSHIFTER_COMMITDEFERREDSETTINGS (EAXPITCHSHIFTER_NONE | \ + EAXPITCHSHIFTER_IMMEDIATE) + +// Use this structure for EAXPITCHSHIFTER_ALLPARAMETERS +typedef struct _EAXPITCHSHIFTERPROPERTIES +{ + long lCoarseTune; // Amount of pitch shift (semitones) + long lFineTune; // Amount of pitch shift (cents) +} EAXPITCHSHIFTERPROPERTIES, *LPEAXPITCHSHIFTERPROPERTIES; + +// Property ranges and defaults: + +#define EAXPITCHSHIFTER_MINCOARSETUNE (-12) +#define EAXPITCHSHIFTER_MAXCOARSETUNE 12 +#define EAXPITCHSHIFTER_DEFAULTCOARSETUNE 12 + +#define EAXPITCHSHIFTER_MINFINETUNE (-50) +#define EAXPITCHSHIFTER_MAXFINETUNE 50 +#define EAXPITCHSHIFTER_DEFAULTFINETUNE 0 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Ring Modulator Effect + +// EAX RING MODULATOR {B89FE60-AFB5-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_RINGMODULATOR_EFFECT, + 0xb89fe60, + 0xafb5, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Ring Modulator properties +typedef enum +{ + EAXRINGMODULATOR_NONE, + EAXRINGMODULATOR_ALLPARAMETERS, + EAXRINGMODULATOR_FREQUENCY, + EAXRINGMODULATOR_HIGHPASSCUTOFF, + EAXRINGMODULATOR_WAVEFORM +} EAXRINGMODULATOR_PROPERTY; + +// OR these flags with property id +#define EAXRINGMODULATOR_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXRINGMODULATOR_DEFERRED 0x80000000 // changes take effect later +#define EAXRINGMODULATOR_COMMITDEFERREDSETTINGS (EAXRINGMODULATOR_NONE | \ + EAXRINGMODULATOR_IMMEDIATE) + +// used by EAXRINGMODULATOR_WAVEFORM +enum +{ + EAX_RINGMODULATOR_SINUSOID, + EAX_RINGMODULATOR_SAWTOOTH, + EAX_RINGMODULATOR_SQUARE +}; + +// Use this structure for EAXRINGMODULATOR_ALLPARAMETERS +typedef struct _EAXRINGMODULATORPROPERTIES +{ + float flFrequency; // Frequency of modulation (Hz) + float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz) + unsigned long ulWaveform; // Waveform selector - see enum above +} EAXRINGMODULATORPROPERTIES, *LPEAXRINGMODULATORPROPERTIES; + +// Property ranges and defaults: + +#define EAXRINGMODULATOR_MINFREQUENCY 0.0f +#define EAXRINGMODULATOR_MAXFREQUENCY 8000.0f +#define EAXRINGMODULATOR_DEFAULTFREQUENCY 440.0f + +#define EAXRINGMODULATOR_MINHIGHPASSCUTOFF 0.0f +#define EAXRINGMODULATOR_MAXHIGHPASSCUTOFF 24000.0f +#define EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF 800.0f + +#define EAXRINGMODULATOR_MINWAVEFORM 0 +#define EAXRINGMODULATOR_MAXWAVEFORM 2 +#define EAXRINGMODULATOR_DEFAULTWAVEFORM 0 + +//////////////////////////////////////////////////////////////////////////// + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // __SEGAEAX_H diff --git a/src/libsegaapi/segaerr.h b/src/libsegaapi/segaerr.h new file mode 100644 index 0000000..f064327 --- /dev/null +++ b/src/libsegaapi/segaerr.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * Copyright (C) 2004 Creative Technology Ltd. All rights reserved. + * + **************************************************************************** + * File: sapidef.h + * + * This file contains the return codes definition for segaapi. + * + **************************************************************************** + */ + + +#ifndef __SEGAAPIERROR_H +#define __SEGAAPIERROR_H + +typedef int SEGASTATUS; + +#define SEGA_SUCCEEDED(_x) ((SEGASTATUS) (_x) >= 0) +#define SEGA_FAILED(_x) ((SEGASTATUS) (_x) < 0) + +#define SEGARESULT_SUCCESS(_x) (_x) +#define SEGARESULT_FAILURE(_x) ((1 << 31) | 0xA000 | (_x)) + +#define SEGA_SUCCESS 0L + + +#define SEGAERR_FAIL SEGARESULT_FAILURE(0) +#define SEGAERR_BAD_POINTER SEGARESULT_FAILURE(3) +#define SEGAERR_UNSUPPORTED SEGARESULT_FAILURE(5) +#define SEGAERR_BAD_PARAM SEGARESULT_FAILURE(9) +#define SEGAERR_INVALID_CHANNEL SEGARESULT_FAILURE(10) +#define SEGAERR_INVALID_SEND SEGARESULT_FAILURE(11) +#define SEGAERR_PLAYING SEGARESULT_FAILURE(12) +#define SEGAERR_NO_RESOURCES SEGARESULT_FAILURE(13) +#define SEGAERR_BAD_CONFIG SEGARESULT_FAILURE(14) +#define SEGAERR_BAD_HANDLE SEGARESULT_FAILURE(18) +#define SEGAERR_BAD_SAMPLERATE SEGARESULT_FAILURE(28) +#define SEGAERR_OUT_OF_MEMORY SEGARESULT_FAILURE(31) +#define SEGAERR_INIT_FAILED SEGARESULT_FAILURE(39) + + +#endif /* __SEGAAPIERROR_H */ + diff --git a/src/libsegaapi/tsf.h b/src/libsegaapi/tsf.h new file mode 100644 index 0000000..6bccab0 --- /dev/null +++ b/src/libsegaapi/tsf.h @@ -0,0 +1,1640 @@ +/* TinySoundFont - v0.8 - SoundFont2 synthesizer - https://github.com/schellingb/TinySoundFont +no warranty implied; use at your own risk +Do this: +#define TSF_IMPLEMENTATION +before you include this file in *one* C or C++ file to create the implementation. +// i.e. it should look like this: +#include ... +#include ... +#define TSF_IMPLEMENTATION +#include "tsf.h" + +[OPTIONAL] #define TSF_NO_STDIO to remove stdio dependency +[OPTIONAL] #define TSF_MALLOC, TSF_REALLOC, and TSF_FREE to avoid stdlib.h +[OPTIONAL] #define TSF_MEMCPY, TSF_MEMSET to avoid string.h +[OPTIONAL] #define TSF_POW, TSF_POWF, TSF_EXPF, TSF_LOG, TSF_TAN, TSF_LOG10, TSF_SQRT to avoid math.h + +NOT YET IMPLEMENTED +- Support for ChorusEffectsSend and ReverbEffectsSend generators +- Better low-pass filter without lowering performance too much +- Support for modulators + +LICENSE (MIT) + +Copyright (C) 2017, 2018 Bernhard Schelling +Based on SFZero, Copyright (C) 2012 Steve Folta (https://github.com/stevefolta/SFZero) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef TSF_INCLUDE_TSF_INL +#define TSF_INCLUDE_TSF_INL + +#ifdef __cplusplus +extern "C" { +# define CPP_DEFAULT0 = 0 +#else +# define CPP_DEFAULT0 +#endif + +//define this if you want the API functions to be static +#ifdef TSF_STATIC +#define TSFDEF static +#else +#define TSFDEF extern +#endif + +// The load functions will return a pointer to a struct tsf which all functions +// thereafter take as the first parameter. +// On error the tsf_load* functions will return NULL most likely due to invalid +// data (or if the file did not exist in tsf_load_filename). +typedef struct tsf tsf; + +#ifndef TSF_NO_STDIO +// Directly load a SoundFont from a .sf2 file path +TSFDEF tsf* tsf_load_filename(const char* filename); +#endif + +// Load a SoundFont from a block of memory +TSFDEF tsf* tsf_load_memory(const void* buffer, int size); + +// Stream structure for the generic loading +struct tsf_stream +{ + // Custom data given to the functions as the first parameter + void* data; + + // Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes) + int(*read)(void* data, void* ptr, unsigned int size); + + // Function pointer will be called to skip ahead over 'count' bytes (returns 1 on success, 0 on error) + int(*skip)(void* data, unsigned int count); +}; + +// Generic SoundFont loading method using the stream structure above +TSFDEF tsf* tsf_load(struct tsf_stream* stream); + +// Free the memory related to this tsf instance +TSFDEF void tsf_close(tsf* f); + +// Stop all playing notes immediatly and reset all channel parameters +TSFDEF void tsf_reset(tsf* f); + +// Returns the preset index from a bank and preset number, or -1 if it does not exist in the loaded SoundFont +TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number); + +// Returns the number of presets in the loaded SoundFont +TSFDEF int tsf_get_presetcount(const tsf* f); + +// Returns the name of a preset index >= 0 and < tsf_get_presetcount() +TSFDEF const char* tsf_get_presetname(const tsf* f, int preset_index); + +// Returns the name of a preset by bank and preset number +TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number); + +// Supported output modes by the render methods +enum TSFOutputMode +{ + // Two channels with single left/right samples one after another + TSF_STEREO_INTERLEAVED, + // Two channels with all samples for the left channel first then right + TSF_STEREO_UNWEAVED, + // A single channel (stereo instruments are mixed into center) + TSF_MONO, +}; + +// Thread safety: +// Your audio output which calls the tsf_render* functions will most likely +// run on a different thread than where the playback tsf_note* functions +// are called. In which case some sort of concurrency control like a +// mutex needs to be used so they are not called at the same time. + +// Setup the parameters for the voice render methods +// outputmode: if mono or stereo and how stereo channel data is ordered +// samplerate: the number of samples per second (output frequency) +// global_gain_db: volume gain in decibels (>0 means higher, <0 means lower) +TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db CPP_DEFAULT0); + +// Start playing a note +// preset_index: preset index >= 0 and < tsf_get_presetcount() +// key: note value between 0 and 127 (60 being middle C) +// vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) +// bank: instrument bank number (alternative to preset_index) +// preset_number: preset number (alternative to preset_index) +// (bank_note_on returns 0 if preset does not exist, otherwise 1) +TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel); +TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel); + +// Stop playing a note +// (bank_note_off returns 0 if preset does not exist, otherwise 1) +TSFDEF void tsf_note_off(tsf* f, int preset_index, int key); +TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key); + +// Stop playing all notes (end with sustain and release) +TSFDEF void tsf_note_off_all(tsf* f); + +// Render output samples into a buffer +// You can either render as signed 16-bit values (tsf_render_short) or +// as 32-bit float values (tsf_render_float) +// buffer: target buffer of size samples * output_channels * sizeof(type) +// samples: number of samples to render +// flag_mixing: if 0 clear the buffer first, otherwise mix into existing data +TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing CPP_DEFAULT0); +TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing CPP_DEFAULT0); + +// Higher level channel based functions, set up channel parameters +// channel: channel number +// preset_index: preset index >= 0 and < tsf_get_presetcount() +// preset_number: preset number (alternative to preset_index) +// flag_mididrums: 0 for normal channels, otherwise apply MIDI drum channel rules +// bank: instrument bank number (alternative to preset_index) +// pan: stereo panning value from 0.0 (left) to 1.0 (right) (default 0.5 center) +// volume: linear volume scale factor (default 1.0 full) +// pitch_wheel: pitch wheel position 0 to 16383 (default 8192 unpitched) +// pitch_range: range of the pitch wheel in semitones (default 2.0) +// tuning: tuning of all playing voices in semitones (default 0.0 standard (A440) tuning) +// (set_preset_number and set_bank_preset return 0 if preset does not exist, otherwise 1) +TSFDEF void tsf_channel_set_presetindex(tsf* f, int channel, int preset_index); +TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums CPP_DEFAULT0); +TSFDEF void tsf_channel_set_bank(tsf* f, int channel, int bank); +TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number); +TSFDEF void tsf_channel_set_pan(tsf* f, int channel, float pan); +TSFDEF void tsf_channel_set_volume(tsf* f, int channel, float volume); +TSFDEF void tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel); +TSFDEF void tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range); +TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning); + +// Start or stop playing notes on a channel (needs channel preset to be set) +// channel: channel number +// key: note value between 0 and 127 (60 being middle C) +// vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) +TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel); +TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key); +TSFDEF void tsf_channel_note_off_all(tsf* f, int channel); //end with sustain and release +TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel); //end immediatly + + // Apply a MIDI control change to the channel (not all controllers are supported!) +TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value); + +// Get current values set on the channels +TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel); +TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel); +TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel); +TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel); +TSFDEF float tsf_channel_get_pan(tsf* f, int channel); +TSFDEF float tsf_channel_get_volume(tsf* f, int channel); + +#ifdef __cplusplus +# undef CPP_DEFAULT0 +} +#endif + +// end header +// --------------------------------------------------------------------------------------------------------- +#endif //TSF_INCLUDE_TSF_INL + +#ifdef TSF_IMPLEMENTATION + +// The lower this block size is the more accurate the effects are. +// Increasing the value significantly lowers the CPU usage of the voice rendering. +// If LFO affects the low-pass filter it can be hearable even as low as 8. +#ifndef TSF_RENDER_EFFECTSAMPLEBLOCK +#define TSF_RENDER_EFFECTSAMPLEBLOCK 64 +#endif + +// Grace release time for quick voice off (avoid clicking noise) +#define TSF_FASTRELEASETIME 0.01f + +#if !defined(TSF_MALLOC) || !defined(TSF_FREE) || !defined(TSF_REALLOC) +# include +# define TSF_MALLOC malloc +# define TSF_FREE free +# define TSF_REALLOC realloc +#endif + +#if !defined(TSF_MEMCPY) || !defined(TSF_MEMSET) +# include +# define TSF_MEMCPY memcpy +# define TSF_MEMSET memset +#endif + +#if !defined(TSF_POW) || !defined(TSF_POWF) || !defined(TSF_EXPF) || !defined(TSF_LOG) || !defined(TSF_TAN) || !defined(TSF_LOG10) || !defined(TSF_SQRT) +# include +# if !defined(__cplusplus) && !defined(NAN) && !defined(powf) && !defined(expf) && !defined(sqrtf) +# define powf (float)pow // deal with old math.h +# define expf (float)exp // files that come without +# define sqrtf (float)sqrt // powf, expf and sqrtf +# endif +# define TSF_POW pow +# define TSF_POWF powf +# define TSF_EXPF expf +# define TSF_LOG log +# define TSF_TAN tan +# define TSF_LOG10 log10 +# define TSF_SQRTF sqrtf +#endif + +#ifndef TSF_NO_STDIO +# include +#endif + +#define TSF_TRUE 1 +#define TSF_FALSE 0 +#define TSF_BOOL char +#define TSF_PI 3.14159265358979323846264338327950288 +#define TSF_NULL 0 + +#ifdef __cplusplus +extern "C" { +#endif + + typedef char tsf_fourcc[4]; + typedef signed char tsf_s8; + typedef unsigned char tsf_u8; + typedef unsigned short tsf_u16; + typedef signed short tsf_s16; + typedef unsigned int tsf_u32; + typedef char tsf_char20[20]; + +#define TSF_FourCCEquals(value1, value2) (value1[0] == value2[0] && value1[1] == value2[1] && value1[2] == value2[2] && value1[3] == value2[3]) + + struct tsf + { + struct tsf_preset* presets; + float* fontSamples; + struct tsf_voice* voices; + struct tsf_channels* channels; + float* outputSamples; + + int presetNum; + int voiceNum; + int outputSampleSize; + unsigned int voicePlayIndex; + + enum TSFOutputMode outputmode; + float outSampleRate; + float globalGainDB; + }; + +#ifndef TSF_NO_STDIO + static int tsf_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); } + static int tsf_stream_stdio_skip(FILE* f, unsigned int count) { return !fseek(f, count, SEEK_CUR); } + TSFDEF tsf* tsf_load_filename(const char* filename) + { + tsf* res; + struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_stdio_read, (int(*)(void*,unsigned int))&tsf_stream_stdio_skip }; +#if __STDC_WANT_SECURE_LIB__ + FILE* f = TSF_NULL; fopen_s(&f, filename, "rb"); +#else + FILE* f = fopen(filename, "rb"); +#endif + if (!f) + { + //if (e) *e = TSF_FILENOTFOUND; + return TSF_NULL; + } + stream.data = f; + res = tsf_load(&stream); + fclose(f); + return res; + } +#endif + + struct tsf_stream_memory { const char* buffer; unsigned int total, pos; }; + static int tsf_stream_memory_read(struct tsf_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TSF_MEMCPY(ptr, m->buffer + m->pos, size); m->pos += size; return size; } + static int tsf_stream_memory_skip(struct tsf_stream_memory* m, unsigned int count) { if (m->pos + count > m->total) return 0; m->pos += count; return 1; } + TSFDEF tsf* tsf_load_memory(const void* buffer, int size) + { + struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_memory_read, (int(*)(void*,unsigned int))&tsf_stream_memory_skip }; + struct tsf_stream_memory f = { 0, 0, 0 }; + f.buffer = (const char*)buffer; + f.total = size; + stream.data = &f; + return tsf_load(&stream); + } + + enum { TSF_LOOPMODE_NONE, TSF_LOOPMODE_CONTINUOUS, TSF_LOOPMODE_SUSTAIN }; + + enum { TSF_SEGMENT_NONE, TSF_SEGMENT_DELAY, TSF_SEGMENT_ATTACK, TSF_SEGMENT_HOLD, TSF_SEGMENT_DECAY, TSF_SEGMENT_SUSTAIN, TSF_SEGMENT_RELEASE, TSF_SEGMENT_DONE }; + + struct tsf_hydra + { + struct tsf_hydra_phdr *phdrs; struct tsf_hydra_pbag *pbags; struct tsf_hydra_pmod *pmods; + struct tsf_hydra_pgen *pgens; struct tsf_hydra_inst *insts; struct tsf_hydra_ibag *ibags; + struct tsf_hydra_imod *imods; struct tsf_hydra_igen *igens; struct tsf_hydra_shdr *shdrs; + int phdrNum, pbagNum, pmodNum, pgenNum, instNum, ibagNum, imodNum, igenNum, shdrNum; + }; + + union tsf_hydra_genamount { struct { tsf_u8 lo, hi; } range; tsf_s16 shortAmount; tsf_u16 wordAmount; }; + struct tsf_hydra_phdr { tsf_char20 presetName; tsf_u16 preset, bank, presetBagNdx; tsf_u32 library, genre, morphology; }; + struct tsf_hydra_pbag { tsf_u16 genNdx, modNdx; }; + struct tsf_hydra_pmod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; }; + struct tsf_hydra_pgen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; }; + struct tsf_hydra_inst { tsf_char20 instName; tsf_u16 instBagNdx; }; + struct tsf_hydra_ibag { tsf_u16 instGenNdx, instModNdx; }; + struct tsf_hydra_imod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; }; + struct tsf_hydra_igen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; }; + struct tsf_hydra_shdr { tsf_char20 sampleName; tsf_u32 start, end, startLoop, endLoop, sampleRate; tsf_u8 originalPitch; tsf_s8 pitchCorrection; tsf_u16 sampleLink, sampleType; }; + +#define TSFR(FIELD) stream->read(stream->data, &i->FIELD, sizeof(i->FIELD)); + static void tsf_hydra_read_phdr(struct tsf_hydra_phdr* i, struct tsf_stream* stream) { TSFR(presetName) TSFR(preset) TSFR(bank) TSFR(presetBagNdx) TSFR(library) TSFR(genre) TSFR(morphology) } + static void tsf_hydra_read_pbag(struct tsf_hydra_pbag* i, struct tsf_stream* stream) { TSFR(genNdx) TSFR(modNdx) } + static void tsf_hydra_read_pmod(struct tsf_hydra_pmod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) } + static void tsf_hydra_read_pgen(struct tsf_hydra_pgen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) } + static void tsf_hydra_read_inst(struct tsf_hydra_inst* i, struct tsf_stream* stream) { TSFR(instName) TSFR(instBagNdx) } + static void tsf_hydra_read_ibag(struct tsf_hydra_ibag* i, struct tsf_stream* stream) { TSFR(instGenNdx) TSFR(instModNdx) } + static void tsf_hydra_read_imod(struct tsf_hydra_imod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) } + static void tsf_hydra_read_igen(struct tsf_hydra_igen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) } + static void tsf_hydra_read_shdr(struct tsf_hydra_shdr* i, struct tsf_stream* stream) { TSFR(sampleName) TSFR(start) TSFR(end) TSFR(startLoop) TSFR(endLoop) TSFR(sampleRate) TSFR(originalPitch) TSFR(pitchCorrection) TSFR(sampleLink) TSFR(sampleType) } +#undef TSFR + + struct tsf_riffchunk { tsf_fourcc id; tsf_u32 size; }; + struct tsf_envelope { float delay, attack, hold, decay, sustain, release, keynumToHold, keynumToDecay; }; + struct tsf_voice_envelope { float level, slope; int samplesUntilNextSegment; short segment, midiVelocity; struct tsf_envelope parameters; TSF_BOOL segmentIsExponential, isAmpEnv; }; + struct tsf_voice_lowpass { double QInv, a0, a1, b1, b2, z1, z2; TSF_BOOL active; }; + struct tsf_voice_lfo { int samplesUntil; float level, delta; }; + + struct tsf_region + { + int loop_mode; + unsigned int sample_rate; + unsigned char lokey, hikey, lovel, hivel; + unsigned int group, offset, end, loop_start, loop_end; + int transpose, tune, pitch_keycenter, pitch_keytrack; + float volume, pan; + struct tsf_envelope ampenv, modenv; + int initialFilterQ, initialFilterFc; + int modEnvToPitch, modEnvToFilterFc, modLfoToFilterFc, modLfoToVolume; + float delayModLFO; + int freqModLFO, modLfoToPitch; + float delayVibLFO; + int freqVibLFO, vibLfoToPitch; + }; + + struct tsf_preset + { + tsf_char20 presetName; + tsf_u16 preset, bank; + struct tsf_region* regions; + int regionNum; + }; + + struct tsf_voice + { + int playingPreset, playingKey, playingChannel; + struct tsf_region* region; + double pitchInputTimecents, pitchOutputFactor; + double sourceSamplePosition; + float noteGainDB, panFactorLeft, panFactorRight; + unsigned int playIndex, loopStart, loopEnd; + struct tsf_voice_envelope ampenv, modenv; + struct tsf_voice_lowpass lowpass; + struct tsf_voice_lfo modlfo, viblfo; + }; + + struct tsf_channel + { + unsigned short presetIndex, bank, pitchWheel, midiPan, midiVolume, midiExpression, midiRPN, midiData; + float panOffset, gainDB, pitchRange, tuning; + }; + + struct tsf_channels + { + void(*setupVoice)(tsf* f, struct tsf_voice* voice); + struct tsf_channel* channels; + int channelNum, activeChannel; + }; + + static double tsf_timecents2Secsd(double timecents) { return TSF_POW(2.0, timecents / 1200.0); } + static float tsf_timecents2Secsf(float timecents) { return TSF_POWF(2.0f, timecents / 1200.0f); } + static float tsf_cents2Hertz(float cents) { return 8.176f * TSF_POWF(2.0f, cents / 1200.0f); } + static float tsf_decibelsToGain(float db) { return (db > -100.f ? TSF_POWF(10.0f, db * 0.05f) : 0); } + static float tsf_gainToDecibels(float gain) { return (gain <= .00001f ? -100.f : (float)(20.0 * TSF_LOG10(gain))); } + + static TSF_BOOL tsf_riffchunk_read(struct tsf_riffchunk* parent, struct tsf_riffchunk* chunk, struct tsf_stream* stream) + { + TSF_BOOL IsRiff, IsList; + if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) > parent->size) return TSF_FALSE; + if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE; + if (!stream->read(stream->data, &chunk->size, sizeof(tsf_u32))) return TSF_FALSE; + if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size > parent->size) return TSF_FALSE; + if (parent) parent->size -= sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size; + IsRiff = TSF_FourCCEquals(chunk->id, "RIFF"), IsList = TSF_FourCCEquals(chunk->id, "LIST"); + if (IsRiff && parent) return TSF_FALSE; //not allowed + if (!IsRiff && !IsList) return TSF_TRUE; //custom type without sub type + if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE; + chunk->size -= sizeof(tsf_fourcc); + return TSF_TRUE; + } + + static void tsf_region_clear(struct tsf_region* i, TSF_BOOL for_relative) + { + TSF_MEMSET(i, 0, sizeof(struct tsf_region)); + i->hikey = i->hivel = 127; + i->pitch_keycenter = 60; // C4 + if (for_relative) return; + + i->pitch_keytrack = 100; + + i->pitch_keycenter = -1; + + // SF2 defaults in timecents. + i->ampenv.delay = i->ampenv.attack = i->ampenv.hold = i->ampenv.decay = i->ampenv.release = -12000.0f; + i->modenv.delay = i->modenv.attack = i->modenv.hold = i->modenv.decay = i->modenv.release = -12000.0f; + + i->initialFilterFc = 13500; + + i->delayModLFO = -12000.0f; + i->delayVibLFO = -12000.0f; + } + + static void tsf_region_operator(struct tsf_region* region, tsf_u16 genOper, union tsf_hydra_genamount* amount) + { + 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 + }; + switch (genOper) + { + case StartAddrsOffset: region->offset += amount->shortAmount; break; + case EndAddrsOffset: region->end += amount->shortAmount; break; + case StartloopAddrsOffset: region->loop_start += amount->shortAmount; break; + case EndloopAddrsOffset: region->loop_end += amount->shortAmount; break; + case StartAddrsCoarseOffset: region->offset += amount->shortAmount * 32768; break; + case ModLfoToPitch: region->modLfoToPitch = amount->shortAmount; break; + case VibLfoToPitch: region->vibLfoToPitch = amount->shortAmount; break; + case ModEnvToPitch: region->modEnvToPitch = amount->shortAmount; break; + case InitialFilterFc: region->initialFilterFc = amount->shortAmount; break; + case InitialFilterQ: region->initialFilterQ = amount->shortAmount; break; + case ModLfoToFilterFc: region->modLfoToFilterFc = amount->shortAmount; break; + case ModEnvToFilterFc: region->modEnvToFilterFc = amount->shortAmount; break; + case EndAddrsCoarseOffset: region->end += amount->shortAmount * 32768; break; + case ModLfoToVolume: region->modLfoToVolume = amount->shortAmount; break; + case Pan: region->pan = amount->shortAmount / 1000.0f; break; + case DelayModLFO: region->delayModLFO = amount->shortAmount; break; + case FreqModLFO: region->freqModLFO = amount->shortAmount; break; + case DelayVibLFO: region->delayVibLFO = amount->shortAmount; break; + case FreqVibLFO: region->freqVibLFO = amount->shortAmount; break; + case DelayModEnv: region->modenv.delay = amount->shortAmount; break; + case AttackModEnv: region->modenv.attack = amount->shortAmount; break; + case HoldModEnv: region->modenv.hold = amount->shortAmount; break; + case DecayModEnv: region->modenv.decay = amount->shortAmount; break; + case SustainModEnv: region->modenv.sustain = amount->shortAmount; break; + case ReleaseModEnv: region->modenv.release = amount->shortAmount; break; + case KeynumToModEnvHold: region->modenv.keynumToHold = amount->shortAmount; break; + case KeynumToModEnvDecay: region->modenv.keynumToDecay = amount->shortAmount; break; + case DelayVolEnv: region->ampenv.delay = amount->shortAmount; break; + case AttackVolEnv: region->ampenv.attack = amount->shortAmount; break; + case HoldVolEnv: region->ampenv.hold = amount->shortAmount; break; + case DecayVolEnv: region->ampenv.decay = amount->shortAmount; break; + case SustainVolEnv: region->ampenv.sustain = amount->shortAmount; break; + case ReleaseVolEnv: region->ampenv.release = amount->shortAmount; break; + case KeynumToVolEnvHold: region->ampenv.keynumToHold = amount->shortAmount; break; + case KeynumToVolEnvDecay: region->ampenv.keynumToDecay = amount->shortAmount; break; + case KeyRange: region->lokey = amount->range.lo; region->hikey = amount->range.hi; break; + case VelRange: region->lovel = amount->range.lo; region->hivel = amount->range.hi; break; + case StartloopAddrsCoarseOffset: region->loop_start += amount->shortAmount * 32768; break; + case InitialAttenuation: region->volume += amount->shortAmount / 100.0f; break; + case EndloopAddrsCoarseOffset: region->loop_end += amount->shortAmount * 32768; break; + case CoarseTune: region->transpose += amount->shortAmount; break; + case FineTune: region->tune += amount->shortAmount; break; + case SampleModes: region->loop_mode = ((amount->wordAmount & 3) == 3 ? TSF_LOOPMODE_SUSTAIN : ((amount->wordAmount & 3) == 1 ? TSF_LOOPMODE_CONTINUOUS : TSF_LOOPMODE_NONE)); break; + case ScaleTuning: region->pitch_keytrack = amount->shortAmount; break; + case ExclusiveClass: region->group = amount->wordAmount; break; + case OverridingRootKey: region->pitch_keycenter = amount->shortAmount; break; + //case gen_endOper: break; // Ignore. + //default: addUnsupportedOpcode(generator_name); + } + } + + static void tsf_region_envtosecs(struct tsf_envelope* p, TSF_BOOL sustainIsGain) + { + // EG times need to be converted from timecents to seconds. + // Pin very short EG segments. Timecents don't get to zero, and our EG is + // happier with zero values. + p->delay = (p->delay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->delay)); + p->attack = (p->attack < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->attack)); + p->release = (p->release < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->release)); + + // If we have dynamic hold or decay times depending on key number we need + // to keep the values in timecents so we can calculate it during startNote + if (!p->keynumToHold) p->hold = (p->hold < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->hold)); + if (!p->keynumToDecay) p->decay = (p->decay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->decay)); + + if (p->sustain < 0.0f) p->sustain = 0.0f; + else if (sustainIsGain) p->sustain = tsf_decibelsToGain(-p->sustain / 10.0f); + else p->sustain = 1.0f - (p->sustain / 1000.0f); + } + + static void tsf_load_presets(tsf* res, struct tsf_hydra *hydra, unsigned int fontSampleCount) + { + enum { GenInstrument = 41, GenKeyRange = 43, GenVelRange = 44, GenSampleID = 53 }; + // Read each preset. + struct tsf_hydra_phdr *pphdr, *pphdrMax; + for (pphdr = hydra->phdrs, pphdrMax = pphdr + hydra->phdrNum - 1; pphdr != pphdrMax; pphdr++) + { + int sortedIndex = 0, region_index = 0; + struct tsf_hydra_phdr *otherphdr; + struct tsf_preset* preset; + struct tsf_hydra_pbag *ppbag, *ppbagEnd; + struct tsf_region globalRegion; + for (otherphdr = hydra->phdrs; otherphdr != pphdrMax; otherphdr++) + { + if (otherphdr == pphdr || otherphdr->bank > pphdr->bank) continue; + else if (otherphdr->bank < pphdr->bank) sortedIndex++; + else if (otherphdr->preset > pphdr->preset) continue; + else if (otherphdr->preset < pphdr->preset) sortedIndex++; + else if (otherphdr < pphdr) sortedIndex++; + } + + preset = &res->presets[sortedIndex]; + TSF_MEMCPY(preset->presetName, pphdr->presetName, sizeof(preset->presetName)); + preset->presetName[sizeof(preset->presetName) - 1] = '\0'; //should be zero terminated in source file but make sure + preset->bank = pphdr->bank; + preset->preset = pphdr->preset; + preset->regionNum = 0; + + //count regions covered by this preset + for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++) + { + unsigned char plokey = 0, phikey = 127, plovel = 0, phivel = 127; + struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd; + for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++) + { + if (ppgen->genOper == GenKeyRange) { plokey = ppgen->genAmount.range.lo; phikey = ppgen->genAmount.range.hi; continue; } + if (ppgen->genOper == GenVelRange) { plovel = ppgen->genAmount.range.lo; phivel = ppgen->genAmount.range.hi; continue; } + if (ppgen->genOper != GenInstrument) continue; + if (ppgen->genAmount.wordAmount >= hydra->instNum) continue; + pinst = hydra->insts + ppgen->genAmount.wordAmount; + for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++) + { + unsigned char ilokey = 0, ihikey = 127, ilovel = 0, ihivel = 127; + for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++) + { + if (pigen->genOper == GenKeyRange) { ilokey = pigen->genAmount.range.lo; ihikey = pigen->genAmount.range.hi; continue; } + if (pigen->genOper == GenVelRange) { ilovel = pigen->genAmount.range.lo; ihivel = pigen->genAmount.range.hi; continue; } + if (pigen->genOper == GenSampleID && ihikey >= plokey && ilokey <= phikey && ihivel >= plovel && ilovel <= phivel) preset->regionNum++; + } + } + } + } + + preset->regions = (struct tsf_region*)TSF_MALLOC(preset->regionNum * sizeof(struct tsf_region)); + tsf_region_clear(&globalRegion, TSF_TRUE); + + // Zones. + for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++) + { + struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd; + struct tsf_region presetRegion = globalRegion; + int hadGenInstrument = 0; + + // Generators. + for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++) + { + // Instrument. + if (ppgen->genOper == GenInstrument) + { + struct tsf_region instRegion; + tsf_u16 whichInst = ppgen->genAmount.wordAmount; + if (whichInst >= hydra->instNum) continue; + + tsf_region_clear(&instRegion, TSF_FALSE); + pinst = &hydra->insts[whichInst]; + for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++) + { + // Generators. + struct tsf_region zoneRegion = instRegion; + int hadSampleID = 0; + for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++) + { + if (pigen->genOper == GenSampleID) + { + struct tsf_hydra_shdr* pshdr; + + //preset region key and vel ranges are a filter for the zone regions + if (zoneRegion.hikey < presetRegion.lokey || zoneRegion.lokey > presetRegion.hikey) continue; + if (zoneRegion.hivel < presetRegion.lovel || zoneRegion.lovel > presetRegion.hivel) continue; + if (presetRegion.lokey > zoneRegion.lokey) zoneRegion.lokey = presetRegion.lokey; + if (presetRegion.hikey < zoneRegion.hikey) zoneRegion.hikey = presetRegion.hikey; + if (presetRegion.lovel > zoneRegion.lovel) zoneRegion.lovel = presetRegion.lovel; + if (presetRegion.hivel < zoneRegion.hivel) zoneRegion.hivel = presetRegion.hivel; + + //sum regions + zoneRegion.offset += presetRegion.offset; + zoneRegion.end += presetRegion.end; + zoneRegion.loop_start += presetRegion.loop_start; + zoneRegion.loop_end += presetRegion.loop_end; + zoneRegion.transpose += presetRegion.transpose; + zoneRegion.tune += presetRegion.tune; + zoneRegion.pitch_keytrack += presetRegion.pitch_keytrack; + zoneRegion.volume += presetRegion.volume; + zoneRegion.pan += presetRegion.pan; + zoneRegion.ampenv.delay += presetRegion.ampenv.delay; + zoneRegion.ampenv.attack += presetRegion.ampenv.attack; + zoneRegion.ampenv.hold += presetRegion.ampenv.hold; + zoneRegion.ampenv.decay += presetRegion.ampenv.decay; + zoneRegion.ampenv.sustain += presetRegion.ampenv.sustain; + zoneRegion.ampenv.release += presetRegion.ampenv.release; + zoneRegion.modenv.delay += presetRegion.modenv.delay; + zoneRegion.modenv.attack += presetRegion.modenv.attack; + zoneRegion.modenv.hold += presetRegion.modenv.hold; + zoneRegion.modenv.decay += presetRegion.modenv.decay; + zoneRegion.modenv.sustain += presetRegion.modenv.sustain; + zoneRegion.modenv.release += presetRegion.modenv.release; + zoneRegion.initialFilterQ += presetRegion.initialFilterQ; + zoneRegion.initialFilterFc += presetRegion.initialFilterFc; + zoneRegion.modEnvToPitch += presetRegion.modEnvToPitch; + zoneRegion.modEnvToFilterFc += presetRegion.modEnvToFilterFc; + zoneRegion.delayModLFO += presetRegion.delayModLFO; + zoneRegion.freqModLFO += presetRegion.freqModLFO; + zoneRegion.modLfoToPitch += presetRegion.modLfoToPitch; + zoneRegion.modLfoToFilterFc += presetRegion.modLfoToFilterFc; + zoneRegion.modLfoToVolume += presetRegion.modLfoToVolume; + zoneRegion.delayVibLFO += presetRegion.delayVibLFO; + zoneRegion.freqVibLFO += presetRegion.freqVibLFO; + zoneRegion.vibLfoToPitch += presetRegion.vibLfoToPitch; + + // EG times need to be converted from timecents to seconds. + tsf_region_envtosecs(&zoneRegion.ampenv, TSF_TRUE); + tsf_region_envtosecs(&zoneRegion.modenv, TSF_FALSE); + + // LFO times need to be converted from timecents to seconds. + zoneRegion.delayModLFO = (zoneRegion.delayModLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayModLFO)); + zoneRegion.delayVibLFO = (zoneRegion.delayVibLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayVibLFO)); + + // Pin values to their ranges. + if (zoneRegion.pan < -0.5f) zoneRegion.pan = -0.5f; + else if (zoneRegion.pan > 0.5f) zoneRegion.pan = 0.5f; + if (zoneRegion.initialFilterQ < 1500 || zoneRegion.initialFilterQ > 13500) zoneRegion.initialFilterQ = 0; + + pshdr = &hydra->shdrs[pigen->genAmount.wordAmount]; + zoneRegion.offset += pshdr->start; + zoneRegion.end += pshdr->end; + zoneRegion.loop_start += pshdr->startLoop; + zoneRegion.loop_end += pshdr->endLoop; + if (pshdr->endLoop > 0) zoneRegion.loop_end -= 1; + if (zoneRegion.pitch_keycenter == -1) zoneRegion.pitch_keycenter = pshdr->originalPitch; + zoneRegion.tune += pshdr->pitchCorrection; + zoneRegion.sample_rate = pshdr->sampleRate; + if (zoneRegion.end && zoneRegion.end < fontSampleCount) zoneRegion.end++; + else zoneRegion.end = fontSampleCount; + + preset->regions[region_index] = zoneRegion; + region_index++; + hadSampleID = 1; + } + else tsf_region_operator(&zoneRegion, pigen->genOper, &pigen->genAmount); + } + + // Handle instrument's global zone. + if (pibag == hydra->ibags + pinst->instBagNdx && !hadSampleID) + instRegion = zoneRegion; + + // Modulators (TODO) + //if (ibag->instModNdx < ibag[1].instModNdx) addUnsupportedOpcode("any modulator"); + } + hadGenInstrument = 1; + } + else tsf_region_operator(&presetRegion, ppgen->genOper, &ppgen->genAmount); + } + + // Modulators (TODO) + //if (pbag->modNdx < pbag[1].modNdx) addUnsupportedOpcode("any modulator"); + + // Handle preset's global zone. + if (ppbag == hydra->pbags + pphdr->presetBagNdx && !hadGenInstrument) + globalRegion = presetRegion; + } + } + } + + static void tsf_load_samples(float** fontSamples, unsigned int* fontSampleCount, struct tsf_riffchunk *chunkSmpl, struct tsf_stream* stream) + { + // Read sample data into float format buffer. + float* out; unsigned int samplesLeft, samplesToRead, samplesToConvert; + samplesLeft = *fontSampleCount = chunkSmpl->size / sizeof(short); + out = *fontSamples = (float*)TSF_MALLOC(samplesLeft * sizeof(float)); + for (; samplesLeft; samplesLeft -= samplesToRead) + { + short sampleBuffer[1024], *in = sampleBuffer;; + samplesToRead = (samplesLeft > 1024 ? 1024 : samplesLeft); + stream->read(stream->data, sampleBuffer, samplesToRead * sizeof(short)); + + // Convert from signed 16-bit to float. + for (samplesToConvert = samplesToRead; samplesToConvert > 0; --samplesToConvert) + // If we ever need to compile for big-endian platforms, we'll need to byte-swap here. + *out++ = (float)(*in++ / 32767.0); + } + } + + static void tsf_voice_envelope_nextsegment(struct tsf_voice_envelope* e, short active_segment, float outSampleRate) + { + switch (active_segment) + { + case TSF_SEGMENT_NONE: + e->samplesUntilNextSegment = (int)(e->parameters.delay * outSampleRate); + if (e->samplesUntilNextSegment > 0) + { + e->segment = TSF_SEGMENT_DELAY; + e->segmentIsExponential = TSF_FALSE; + e->level = 0.0; + e->slope = 0.0; + return; + } + case TSF_SEGMENT_DELAY: + e->samplesUntilNextSegment = (int)(e->parameters.attack * outSampleRate); + if (e->samplesUntilNextSegment > 0) + { + if (!e->isAmpEnv) + { + //mod env attack duration scales with velocity (velocity of 1 is full duration, max velocity is 0.125 times duration) + e->samplesUntilNextSegment = (int)(e->parameters.attack * ((145 - e->midiVelocity) / 144.0f) * outSampleRate); + } + e->segment = TSF_SEGMENT_ATTACK; + e->segmentIsExponential = TSF_FALSE; + e->level = 0.0f; + e->slope = 1.0f / e->samplesUntilNextSegment; + return; + } + case TSF_SEGMENT_ATTACK: + e->samplesUntilNextSegment = (int)(e->parameters.hold * outSampleRate); + if (e->samplesUntilNextSegment > 0) + { + e->segment = TSF_SEGMENT_HOLD; + e->segmentIsExponential = TSF_FALSE; + e->level = 1.0f; + e->slope = 0.0f; + return; + } + case TSF_SEGMENT_HOLD: + e->samplesUntilNextSegment = (int)(e->parameters.decay * outSampleRate); + if (e->samplesUntilNextSegment > 0) + { + e->segment = TSF_SEGMENT_DECAY; + e->level = 1.0f; + if (e->isAmpEnv) + { + // I don't truly understand this; just following what LinuxSampler does. + float mysterySlope = -9.226f / e->samplesUntilNextSegment; + e->slope = TSF_EXPF(mysterySlope); + e->segmentIsExponential = TSF_TRUE; + if (e->parameters.sustain > 0.0f) + { + // Again, this is following LinuxSampler's example, which is similar to + // SF2-style decay, where "decay" specifies the time it would take to + // get to zero, not to the sustain level. The SFZ spec is not that + // specific about what "decay" means, so perhaps it's really supposed + // to specify the time to reach the sustain level. + e->samplesUntilNextSegment = (int)(TSF_LOG(e->parameters.sustain) / mysterySlope); + } + } + else + { + e->slope = -1.0f / e->samplesUntilNextSegment; + e->samplesUntilNextSegment = (int)(e->parameters.decay * (1.0f - e->parameters.sustain) * outSampleRate); + e->segmentIsExponential = TSF_FALSE; + } + return; + } + case TSF_SEGMENT_DECAY: + e->segment = TSF_SEGMENT_SUSTAIN; + e->level = e->parameters.sustain; + e->slope = 0.0f; + e->samplesUntilNextSegment = 0x7FFFFFFF; + e->segmentIsExponential = TSF_FALSE; + return; + case TSF_SEGMENT_SUSTAIN: + e->segment = TSF_SEGMENT_RELEASE; + e->samplesUntilNextSegment = (int)((e->parameters.release <= 0 ? TSF_FASTRELEASETIME : e->parameters.release) * outSampleRate); + if (e->isAmpEnv) + { + // I don't truly understand this; just following what LinuxSampler does. + float mysterySlope = -9.226f / e->samplesUntilNextSegment; + e->slope = TSF_EXPF(mysterySlope); + e->segmentIsExponential = TSF_TRUE; + } + else + { + e->slope = -e->level / e->samplesUntilNextSegment; + e->segmentIsExponential = TSF_FALSE; + } + return; + case TSF_SEGMENT_RELEASE: + default: + e->segment = TSF_SEGMENT_DONE; + e->segmentIsExponential = TSF_FALSE; + e->level = e->slope = 0.0f; + e->samplesUntilNextSegment = 0x7FFFFFF; + } + } + + static void tsf_voice_envelope_setup(struct tsf_voice_envelope* e, struct tsf_envelope* new_parameters, int midiNoteNumber, short midiVelocity, TSF_BOOL isAmpEnv, float outSampleRate) + { + e->parameters = *new_parameters; + if (e->parameters.keynumToHold) + { + e->parameters.hold += e->parameters.keynumToHold * (60.0f - midiNoteNumber); + e->parameters.hold = (e->parameters.hold < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.hold)); + } + if (e->parameters.keynumToDecay) + { + e->parameters.decay += e->parameters.keynumToDecay * (60.0f - midiNoteNumber); + e->parameters.decay = (e->parameters.decay < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.decay)); + } + e->midiVelocity = midiVelocity; + e->isAmpEnv = isAmpEnv; + tsf_voice_envelope_nextsegment(e, TSF_SEGMENT_NONE, outSampleRate); + } + + static void tsf_voice_envelope_process(struct tsf_voice_envelope* e, int numSamples, float outSampleRate) + { + if (e->slope) + { + if (e->segmentIsExponential) e->level *= TSF_POWF(e->slope, (float)numSamples); + else e->level += (e->slope * numSamples); + } + if ((e->samplesUntilNextSegment -= numSamples) <= 0) + tsf_voice_envelope_nextsegment(e, e->segment, outSampleRate); + } + + static void tsf_voice_lowpass_setup(struct tsf_voice_lowpass* e, float Fc) + { + // Lowpass filter from http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/ + double K = TSF_TAN(TSF_PI * Fc), KK = K * K; + double norm = 1 / (1 + K * e->QInv + KK); + e->a0 = KK * norm; + e->a1 = 2 * e->a0; + e->b1 = 2 * (KK - 1) * norm; + e->b2 = (1 - K * e->QInv + KK) * norm; + } + + static float tsf_voice_lowpass_process(struct tsf_voice_lowpass* e, double In) + { + double Out = In * e->a0 + e->z1; e->z1 = In * e->a1 + e->z2 - e->b1 * Out; e->z2 = In * e->a0 - e->b2 * Out; return (float)Out; + } + + static void tsf_voice_lfo_setup(struct tsf_voice_lfo* e, float delay, int freqCents, float outSampleRate) + { + e->samplesUntil = (int)(delay * outSampleRate); + e->delta = (4.0f * tsf_cents2Hertz((float)freqCents) / outSampleRate); + e->level = 0; + } + + static void tsf_voice_lfo_process(struct tsf_voice_lfo* e, int blockSamples) + { + if (e->samplesUntil > blockSamples) { e->samplesUntil -= blockSamples; return; } + e->level += e->delta * blockSamples; + if (e->level > 1.0f) { e->delta = -e->delta; e->level = 2.0f - e->level; } + else if (e->level < -1.0f) { e->delta = -e->delta; e->level = -2.0f - e->level; } + } + + static void tsf_voice_kill(struct tsf_voice* v) + { + v->region = TSF_NULL; + v->playingPreset = -1; + } + + static void tsf_voice_end(struct tsf_voice* v, float outSampleRate) + { + tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, outSampleRate); + tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, outSampleRate); + if (v->region->loop_mode == TSF_LOOPMODE_SUSTAIN) + { + // Continue playing, but stop looping. + v->loopEnd = v->loopStart; + } + } + + static void tsf_voice_endquick(struct tsf_voice* v, float outSampleRate) + { + v->ampenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, outSampleRate); + v->modenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, outSampleRate); + } + + static void tsf_voice_calcpitchratio(struct tsf_voice* v, float pitchShift, float outSampleRate) + { + double note = v->playingKey + v->region->transpose + v->region->tune / 100.0; + double adjustedPitch = v->region->pitch_keycenter + (note - v->region->pitch_keycenter) * (v->region->pitch_keytrack / 100.0); + if (pitchShift) adjustedPitch += pitchShift; + v->pitchInputTimecents = adjustedPitch * 100.0; + v->pitchOutputFactor = v->region->sample_rate / (tsf_timecents2Secsd(v->region->pitch_keycenter * 100.0) * outSampleRate); + } + + static void tsf_voice_render(tsf* f, struct tsf_voice* v, float* outputBuffer, int numSamples) + { + struct tsf_region* region = v->region; + float* input = f->fontSamples; + float* outL = outputBuffer; + float* outR = (f->outputmode == TSF_STEREO_UNWEAVED ? outL + numSamples : TSF_NULL); + + // Cache some values, to give them at least some chance of ending up in registers. + TSF_BOOL updateModEnv = (region->modEnvToPitch || region->modEnvToFilterFc); + TSF_BOOL updateModLFO = (v->modlfo.delta && (region->modLfoToPitch || region->modLfoToFilterFc || region->modLfoToVolume)); + TSF_BOOL updateVibLFO = (v->viblfo.delta && (region->vibLfoToPitch)); + TSF_BOOL isLooping = (v->loopStart < v->loopEnd); + unsigned int tmpLoopStart = v->loopStart, tmpLoopEnd = v->loopEnd; + double tmpSampleEndDbl = (double)region->end, tmpLoopEndDbl = (double)tmpLoopEnd + 1.0; + double tmpSourceSamplePosition = v->sourceSamplePosition; + struct tsf_voice_lowpass tmpLowpass = v->lowpass; + + TSF_BOOL dynamicLowpass = (region->modLfoToFilterFc || region->modEnvToFilterFc); + float tmpSampleRate, tmpInitialFilterFc, tmpModLfoToFilterFc, tmpModEnvToFilterFc; + + TSF_BOOL dynamicPitchRatio = (region->modLfoToPitch || region->modEnvToPitch || region->vibLfoToPitch); + double pitchRatio; + float tmpModLfoToPitch, tmpVibLfoToPitch, tmpModEnvToPitch; + + TSF_BOOL dynamicGain = (region->modLfoToVolume != 0); + float noteGain = 0, tmpModLfoToVolume; + + if (dynamicLowpass) tmpSampleRate = f->outSampleRate, tmpInitialFilterFc = (float)region->initialFilterFc, tmpModLfoToFilterFc = (float)region->modLfoToFilterFc, tmpModEnvToFilterFc = (float)region->modEnvToFilterFc; + else tmpSampleRate = 0, tmpInitialFilterFc = 0, tmpModLfoToFilterFc = 0, tmpModEnvToFilterFc = 0; + + if (dynamicPitchRatio) pitchRatio = 0, tmpModLfoToPitch = (float)region->modLfoToPitch, tmpVibLfoToPitch = (float)region->vibLfoToPitch, tmpModEnvToPitch = (float)region->modEnvToPitch; + else pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents) * v->pitchOutputFactor, tmpModLfoToPitch = 0, tmpVibLfoToPitch = 0, tmpModEnvToPitch = 0; + + if (dynamicGain) tmpModLfoToVolume = (float)region->modLfoToVolume * 0.1f; + else noteGain = tsf_decibelsToGain(v->noteGainDB), tmpModLfoToVolume = 0; + + while (numSamples) + { + float gainMono, gainLeft, gainRight; + int blockSamples = (numSamples > TSF_RENDER_EFFECTSAMPLEBLOCK ? TSF_RENDER_EFFECTSAMPLEBLOCK : numSamples); + numSamples -= blockSamples; + + if (dynamicLowpass) + { + float fres = tmpInitialFilterFc + v->modlfo.level * tmpModLfoToFilterFc + v->modenv.level * tmpModEnvToFilterFc; + tmpLowpass.active = (fres <= 13500.0f); + if (tmpLowpass.active) tsf_voice_lowpass_setup(&tmpLowpass, tsf_cents2Hertz(fres) / tmpSampleRate); + } + + if (dynamicPitchRatio) + pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents + (v->modlfo.level * tmpModLfoToPitch + v->viblfo.level * tmpVibLfoToPitch + v->modenv.level * tmpModEnvToPitch)) * v->pitchOutputFactor; + + if (dynamicGain) + noteGain = tsf_decibelsToGain(v->noteGainDB + (v->modlfo.level * tmpModLfoToVolume)); + + gainMono = noteGain * v->ampenv.level; + + // Update EG. + tsf_voice_envelope_process(&v->ampenv, blockSamples, f->outSampleRate); + if (updateModEnv) tsf_voice_envelope_process(&v->modenv, blockSamples, f->outSampleRate); + + // Update LFOs. + if (updateModLFO) tsf_voice_lfo_process(&v->modlfo, blockSamples); + if (updateVibLFO) tsf_voice_lfo_process(&v->viblfo, blockSamples); + + switch (f->outputmode) + { + case TSF_STEREO_INTERLEAVED: + gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight; + while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) + { + unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); + + // Simple linear interpolation. + float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); + + // Low-pass filter. + if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); + + *outL++ += val * gainLeft; + *outL++ += val * gainRight; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); + } + break; + + case TSF_STEREO_UNWEAVED: + gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight; + while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) + { + unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); + + // Simple linear interpolation. + float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); + + // Low-pass filter. + if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); + + *outL++ += val * gainLeft; + *outR++ += val * gainRight; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); + } + break; + + case TSF_MONO: + while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) + { + unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); + + // Simple linear interpolation. + float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); + + // Low-pass filter. + if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); + + *outL++ += val * gainMono; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); + } + break; + } + + if (tmpSourceSamplePosition >= tmpSampleEndDbl || v->ampenv.segment == TSF_SEGMENT_DONE) + { + tsf_voice_kill(v); + return; + } + } + + v->sourceSamplePosition = tmpSourceSamplePosition; + if (tmpLowpass.active || dynamicLowpass) v->lowpass = tmpLowpass; + } + + TSFDEF tsf* tsf_load(struct tsf_stream* stream) + { + tsf* res = TSF_NULL; + struct tsf_riffchunk chunkHead; + struct tsf_riffchunk chunkList; + struct tsf_hydra hydra; + float* fontSamples = TSF_NULL; + unsigned int fontSampleCount; + + if (!tsf_riffchunk_read(TSF_NULL, &chunkHead, stream) || !TSF_FourCCEquals(chunkHead.id, "sfbk")) + { + //if (e) *e = TSF_INVALID_NOSF2HEADER; + return res; + } + + // Read hydra and locate sample data. + TSF_MEMSET(&hydra, 0, sizeof(hydra)); + while (tsf_riffchunk_read(&chunkHead, &chunkList, stream)) + { + struct tsf_riffchunk chunk; + if (TSF_FourCCEquals(chunkList.id, "pdta")) + { + while (tsf_riffchunk_read(&chunkList, &chunk, stream)) + { +#define HandleChunk(chunkName) (TSF_FourCCEquals(chunk.id, #chunkName) && !(chunk.size % chunkName##SizeInFile)) \ + { \ + int num = chunk.size / chunkName##SizeInFile, i; \ + hydra.chunkName##Num = num; \ + hydra.chunkName##s = (struct tsf_hydra_##chunkName*)TSF_MALLOC(num * sizeof(struct tsf_hydra_##chunkName)); \ + for (i = 0; i < num; ++i) tsf_hydra_read_##chunkName(&hydra.chunkName##s[i], stream); \ + } + enum + { + phdrSizeInFile = 38, pbagSizeInFile = 4, pmodSizeInFile = 10, + pgenSizeInFile = 4, instSizeInFile = 22, ibagSizeInFile = 4, + imodSizeInFile = 10, igenSizeInFile = 4, shdrSizeInFile = 46 + }; + if HandleChunk(phdr) else if HandleChunk(pbag) else if HandleChunk(pmod) + else if HandleChunk(pgen) else if HandleChunk(inst) else if HandleChunk(ibag) + else if HandleChunk(imod) else if HandleChunk(igen) else if HandleChunk(shdr) + else stream->skip(stream->data, chunk.size); +#undef HandleChunk + } + } + else if (TSF_FourCCEquals(chunkList.id, "sdta")) + { + while (tsf_riffchunk_read(&chunkList, &chunk, stream)) + { + if (TSF_FourCCEquals(chunk.id, "smpl")) + { + tsf_load_samples(&fontSamples, &fontSampleCount, &chunk, stream); + } + else stream->skip(stream->data, chunk.size); + } + } + else stream->skip(stream->data, chunkList.size); + } + if (!hydra.phdrs || !hydra.pbags || !hydra.pmods || !hydra.pgens || !hydra.insts || !hydra.ibags || !hydra.imods || !hydra.igens || !hydra.shdrs) + { + //if (e) *e = TSF_INVALID_INCOMPLETE; + } + else if (fontSamples == TSF_NULL) + { + //if (e) *e = TSF_INVALID_NOSAMPLEDATA; + } + else + { + res = (tsf*)TSF_MALLOC(sizeof(tsf)); + TSF_MEMSET(res, 0, sizeof(tsf)); + res->presetNum = hydra.phdrNum - 1; + res->presets = (struct tsf_preset*)TSF_MALLOC(res->presetNum * sizeof(struct tsf_preset)); + res->fontSamples = fontSamples; + res->outSampleRate = 44100.0f; + fontSamples = TSF_NULL; //don't free below + tsf_load_presets(res, &hydra, fontSampleCount); + } + TSF_FREE(hydra.phdrs); TSF_FREE(hydra.pbags); TSF_FREE(hydra.pmods); + TSF_FREE(hydra.pgens); TSF_FREE(hydra.insts); TSF_FREE(hydra.ibags); + TSF_FREE(hydra.imods); TSF_FREE(hydra.igens); TSF_FREE(hydra.shdrs); + TSF_FREE(fontSamples); + return res; + } + + TSFDEF void tsf_close(tsf* f) + { + struct tsf_preset *preset, *presetEnd; + if (!f) return; + for (preset = f->presets, presetEnd = preset + f->presetNum; preset != presetEnd; preset++) + TSF_FREE(preset->regions); + TSF_FREE(f->presets); + TSF_FREE(f->fontSamples); + TSF_FREE(f->voices); + if (f->channels) { TSF_FREE(f->channels->channels); TSF_FREE(f->channels); } + TSF_FREE(f->outputSamples); + TSF_FREE(f); + } + + TSFDEF void tsf_reset(tsf* f) + { + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) + if (v->playingPreset != -1 && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release)) + tsf_voice_endquick(v, f->outSampleRate); + if (f->channels) { TSF_FREE(f->channels->channels); TSF_FREE(f->channels); f->channels = TSF_NULL; } + } + + TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number) + { + const struct tsf_preset *presets; + int i, iMax; + for (presets = f->presets, i = 0, iMax = f->presetNum; i < iMax; i++) + if (presets[i].preset == preset_number && presets[i].bank == bank) + return i; + return -1; + } + + TSFDEF int tsf_get_presetcount(const tsf* f) + { + return f->presetNum; + } + + TSFDEF const char* tsf_get_presetname(const tsf* f, int preset) + { + return (preset < 0 || preset >= f->presetNum ? TSF_NULL : f->presets[preset].presetName); + } + + TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number) + { + return tsf_get_presetname(f, tsf_get_presetindex(f, bank, preset_number)); + } + + TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db) + { + f->outputmode = outputmode; + f->outSampleRate = (float)(samplerate >= 1 ? samplerate : 44100.0f); + f->globalGainDB = global_gain_db; + } + + TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel) + { + int midiVelocity = (int)(vel * 127), voicePlayIndex; + struct tsf_region *region, *regionEnd; + + if (preset_index < 0 || preset_index >= f->presetNum) return; + if (vel <= 0.0f) { tsf_note_off(f, preset_index, key); return; } + + // Play all matching regions. + voicePlayIndex = f->voicePlayIndex++; + for (region = f->presets[preset_index].regions, regionEnd = region + f->presets[preset_index].regionNum; region != regionEnd; region++) + { + struct tsf_voice *voice, *v, *vEnd; TSF_BOOL doLoop; float filterQDB; + if (key < region->lokey || key > region->hikey || midiVelocity < region->lovel || midiVelocity > region->hivel) continue; + + voice = TSF_NULL, v = f->voices, vEnd = v + f->voiceNum; + if (region->group) + { + for (; v != vEnd; v++) + if (v->playingPreset == preset_index && v->region->group == region->group) tsf_voice_endquick(v, f->outSampleRate); + else if (v->playingPreset == -1 && !voice) voice = v; + } + else for (; v != vEnd; v++) if (v->playingPreset == -1) { voice = v; break; } + + if (!voice) + { + f->voiceNum += 4; + f->voices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice)); + voice = &f->voices[f->voiceNum - 4]; + voice[1].playingPreset = voice[2].playingPreset = voice[3].playingPreset = -1; + } + + voice->region = region; + voice->playingPreset = preset_index; + voice->playingKey = key; + voice->playIndex = voicePlayIndex; + voice->noteGainDB = f->globalGainDB - region->volume - tsf_gainToDecibels(1.0f / vel); + + if (f->channels) + { + f->channels->setupVoice(f, voice); + } + else + { + tsf_voice_calcpitchratio(voice, 0, f->outSampleRate); + // The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2). + voice->panFactorLeft = TSF_SQRTF(0.5f - region->pan); + voice->panFactorRight = TSF_SQRTF(0.5f + region->pan); + } + + // Offset/end. + voice->sourceSamplePosition = region->offset; + + // Loop. + doLoop = (region->loop_mode != TSF_LOOPMODE_NONE && region->loop_start < region->loop_end); + voice->loopStart = (doLoop ? region->loop_start : 0); + voice->loopEnd = (doLoop ? region->loop_end : 0); + + // Setup envelopes. + tsf_voice_envelope_setup(&voice->ampenv, ®ion->ampenv, key, midiVelocity, TSF_TRUE, f->outSampleRate); + tsf_voice_envelope_setup(&voice->modenv, ®ion->modenv, key, midiVelocity, TSF_FALSE, f->outSampleRate); + + // Setup lowpass filter. + filterQDB = region->initialFilterQ / 10.0f; + voice->lowpass.QInv = 1.0 / TSF_POW(10.0, (filterQDB / 20.0)); + voice->lowpass.z1 = voice->lowpass.z2 = 0; + voice->lowpass.active = (region->initialFilterFc <= 13500); + if (voice->lowpass.active) tsf_voice_lowpass_setup(&voice->lowpass, tsf_cents2Hertz((float)region->initialFilterFc) / f->outSampleRate); + + // Setup LFO filters. + tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, f->outSampleRate); + tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, f->outSampleRate); + } + } + + TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel) + { + int preset_index = tsf_get_presetindex(f, bank, preset_number); + if (preset_index == -1) return 0; + tsf_note_on(f, preset_index, key, vel); + return 1; + } + + TSFDEF void tsf_note_off(tsf* f, int preset_index, int key) + { + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast; + for (; v != vEnd; v++) + { + //Find the first and last entry in the voices list with matching preset, key and look up the smallest play index + if (v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE) continue; + else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v; + else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v; + } + if (!vMatchFirst) return; + for (v = vMatchFirst; v <= vMatchLast; v++) + { + //Stop all voices with matching preset, key and the smallest play index which was enumerated above + if (v != vMatchFirst && v != vMatchLast && + (v->playIndex != vMatchFirst->playIndex || v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue; + tsf_voice_end(v, f->outSampleRate); + } + } + + TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key) + { + int preset_index = tsf_get_presetindex(f, bank, preset_number); + if (preset_index == -1) return 0; + tsf_note_off(f, preset_index, key); + return 1; + } + + TSFDEF void tsf_note_off_all(tsf* f) + { + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) if (v->playingPreset != -1 && v->ampenv.segment < TSF_SEGMENT_RELEASE) + tsf_voice_end(v, f->outSampleRate); + } + + TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing) + { + float *floatSamples; + int channelSamples = (f->outputmode == TSF_MONO ? 1 : 2) * samples, floatBufferSize = channelSamples * sizeof(float); + short* bufferEnd = buffer + channelSamples; + if (floatBufferSize > f->outputSampleSize) + { + TSF_FREE(f->outputSamples); + f->outputSamples = (float*)TSF_MALLOC(floatBufferSize); + f->outputSampleSize = floatBufferSize; + } + + tsf_render_float(f, f->outputSamples, samples, TSF_FALSE); + + floatSamples = f->outputSamples; + if (flag_mixing) + while (buffer != bufferEnd) + { + float v = *floatSamples++; + int vi = *buffer + (v < -1.00004566f ? (int)-32768 : (v > 1.00001514f ? (int)32767 : (int)(v * 32767.5f))); + *buffer++ = (vi < -32768 ? (short)-32768 : (vi > 32767 ? (short)32767 : (short)vi)); + } + else + while (buffer != bufferEnd) + { + float v = *floatSamples++; + *buffer++ = (v < -1.00004566f ? (short)-32768 : (v > 1.00001514f ? (short)32767 : (short)(v * 32767.5f))); + } + } + + TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing) + { + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + if (!flag_mixing) TSF_MEMSET(buffer, 0, (f->outputmode == TSF_MONO ? 1 : 2) * sizeof(float) * samples); + for (; v != vEnd; v++) + if (v->playingPreset != -1) + tsf_voice_render(f, v, buffer, samples); + } + + static void tsf_channel_setup_voice(tsf* f, struct tsf_voice* v) + { + struct tsf_channel* c = &f->channels->channels[f->channels->activeChannel]; + float newpan = v->region->pan + c->panOffset; + v->playingChannel = f->channels->activeChannel; + v->noteGainDB += c->gainDB; + tsf_voice_calcpitchratio(v, (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)), f->outSampleRate); + if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; } + else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; } + else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); } + } + + static struct tsf_channel* tsf_channel_init(tsf* f, int channel) + { + int i; + if (f->channels && channel < f->channels->channelNum) return &f->channels->channels[channel]; + if (!f->channels) + { + f->channels = (struct tsf_channels*)TSF_MALLOC(sizeof(struct tsf_channels)); + f->channels->setupVoice = &tsf_channel_setup_voice; + f->channels->channels = NULL; + f->channels->channelNum = 0; + f->channels->activeChannel = 0; + } + i = f->channels->channelNum; + f->channels->channelNum = channel + 1; + f->channels->channels = (struct tsf_channel*)TSF_REALLOC(f->channels->channels, f->channels->channelNum * sizeof(struct tsf_channel)); + for (; i <= channel; i++) + { + struct tsf_channel* c = &f->channels->channels[i]; + c->presetIndex = c->bank = 0; + c->pitchWheel = c->midiPan = 8192; + c->midiVolume = c->midiExpression = 16383; + c->midiRPN = 0xFFFF; + c->midiData = 0; + c->panOffset = 0.0f; + c->gainDB = 0.0f; + c->pitchRange = 2.0f; + c->tuning = 0.0f; + } + return &f->channels->channels[channel]; + } + + static void tsf_channel_applypitch(tsf* f, int channel, struct tsf_channel* c) + { + struct tsf_voice *v, *vEnd; + float pitchShift = (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)); + for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) + if (v->playingChannel == channel && v->playingPreset != -1) + tsf_voice_calcpitchratio(v, pitchShift, f->outSampleRate); + } + + TSFDEF void tsf_channel_set_presetindex(tsf* f, int channel, int preset_index) + { + tsf_channel_init(f, channel)->presetIndex = (unsigned short)preset_index; + } + + TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums) + { + struct tsf_channel *c = tsf_channel_init(f, channel); + int preset_index; + if (flag_mididrums) + { + preset_index = tsf_get_presetindex(f, 128 | (c->bank & 0x7FFF), preset_number); + if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, preset_number); + if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, 0); + if (preset_index == -1) preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number); + } + else preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number); + if (preset_index == -1) preset_index = tsf_get_presetindex(f, 0, preset_number); + if (preset_index != -1) { c->presetIndex = preset_index; return 1; } + return 0; + } + + TSFDEF void tsf_channel_set_bank(tsf* f, int channel, int bank) + { + tsf_channel_init(f, channel)->bank = bank; + } + + TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number) + { + struct tsf_channel *c = tsf_channel_init(f, channel); + int preset_index = tsf_get_presetindex(f, bank, preset_number); + if (preset_index == -1) return 0; + c->presetIndex = preset_index; + c->bank = bank; + return 1; + } + TSFDEF void tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel) + { + struct tsf_channel *c = tsf_channel_init(f, channel); + if (c->pitchWheel == pitch_wheel) return; + c->pitchWheel = pitch_wheel; + tsf_channel_applypitch(f, channel, c); + } + + TSFDEF void tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range) + { + struct tsf_channel *c = tsf_channel_init(f, channel); + if (c->pitchRange == pitch_range) return; + c->pitchRange = pitch_range; + if (c->pitchWheel != 8192) tsf_channel_applypitch(f, channel, c); + } + + TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning) + { + struct tsf_channel *c = tsf_channel_init(f, channel); + if (c->tuning == tuning) return; + c->tuning = tuning; + tsf_channel_applypitch(f, channel, c); + } + + TSFDEF void tsf_channel_set_pan(tsf* f, int channel, float pan) + { + struct tsf_voice *v, *vEnd; + for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) + if (v->playingChannel == channel && v->playingPreset != -1) + { + float newpan = v->region->pan + pan - 0.5f; + if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; } + else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; } + else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); } + } + tsf_channel_init(f, channel)->panOffset = pan - 0.5f; + } + + TSFDEF void tsf_channel_set_volume(tsf* f, int channel, float volume) + { + struct tsf_channel *c = tsf_channel_init(f, channel); + float gainDB = tsf_gainToDecibels(volume), gainDBChange = gainDB - c->gainDB; + struct tsf_voice *v, *vEnd; + if (gainDBChange == 0) return; + for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) + if (v->playingChannel == channel && v->playingPreset != -1) + v->noteGainDB += gainDBChange; + c->gainDB = gainDB; + } + + TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel) + { + if (!f->channels || channel >= f->channels->channelNum) return; + f->channels->activeChannel = channel; + tsf_note_on(f, f->channels->channels[channel].presetIndex, key, vel); + } + + TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key) + { + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast; + for (; v != vEnd; v++) + { + //Find the first and last entry in the voices list with matching channel, key and look up the smallest play index + if (v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE) continue; + else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v; + else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v; + } + if (!vMatchFirst) return; + for (v = vMatchFirst; v <= vMatchLast; v++) + { + //Stop all voices with matching channel, key and the smallest play index which was enumerated above + if (v != vMatchFirst && v != vMatchLast && + (v->playIndex != vMatchFirst->playIndex || v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue; + tsf_voice_end(v, f->outSampleRate); + } + } + + TSFDEF void tsf_channel_note_off_all(tsf* f, int channel) + { + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) + if (v->playingPreset != -1 && v->playingChannel == channel && v->ampenv.segment < TSF_SEGMENT_RELEASE) + tsf_voice_end(v, f->outSampleRate); + } + + TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel) + { + struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; + for (; v != vEnd; v++) + if (v->playingPreset != -1 && v->playingChannel == channel && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release)) + tsf_voice_endquick(v, f->outSampleRate); + } + + TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value) + { + struct tsf_channel* c = tsf_channel_init(f, channel); + switch (controller) + { + case 7 /*VOLUME_MSB*/: c->midiVolume = (c->midiVolume & 0x7F) | (control_value << 7); goto TCMC_SET_VOLUME; + case 39 /*VOLUME_LSB*/: c->midiVolume = (c->midiVolume & 0x3F80) | control_value; goto TCMC_SET_VOLUME; + case 11 /*EXPRESSION_MSB*/: c->midiExpression = (c->midiExpression & 0x7F) | (control_value << 7); goto TCMC_SET_VOLUME; + case 43 /*EXPRESSION_LSB*/: c->midiExpression = (c->midiExpression & 0x3F80) | control_value; goto TCMC_SET_VOLUME; + case 10 /*PAN_MSB*/: c->midiPan = (c->midiPan & 0x7F) | (control_value << 7); goto TCMC_SET_PAN; + case 42 /*PAN_LSB*/: c->midiPan = (c->midiPan & 0x3F80) | control_value; goto TCMC_SET_PAN; + case 6 /*DATA_ENTRY_MSB*/: c->midiData = (c->midiData & 0x7F) | (control_value << 7); goto TCMC_SET_DATA; + case 38 /*DATA_ENTRY_LSB*/: c->midiData = (c->midiData & 0x3F80) | control_value; goto TCMC_SET_DATA; + case 0 /*BANK_SELECT_MSB*/: c->bank = 0x8000 | control_value; return; //bank select MSB alone acts like LSB + case 32 /*BANK_SELECT_LSB*/: c->bank = (c->bank & 0x8000 ? ((c->bank & 0x7F) << 7) : 0) | control_value; return; + case 101 /*RPN_MSB*/: c->midiRPN = ((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x7F) | (control_value << 7); return; + case 100 /*RPN_LSB*/: c->midiRPN = ((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x3F80) | control_value; return; + case 98 /*NRPN_LSB*/: c->midiRPN = 0xFFFF; return; + case 99 /*NRPN_MSB*/: c->midiRPN = 0xFFFF; return; + case 120 /*ALL_SOUND_OFF*/: tsf_channel_sounds_off_all(f, channel); return; + case 123 /*ALL_NOTES_OFF*/: tsf_channel_note_off_all(f, channel); return; + case 121 /*ALL_CTRL_OFF*/: + c->midiVolume = c->midiExpression = 16383; + c->midiPan = 8192; + c->bank = 0; + tsf_channel_set_volume(f, channel, 1.0f); + tsf_channel_set_pan(f, channel, 0.5f); + tsf_channel_set_pitchrange(f, channel, 2.0f); + return; + } + return; + TCMC_SET_VOLUME: + //Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI + tsf_channel_set_volume(f, channel, TSF_POWF((c->midiVolume / 16383.0f) * (c->midiExpression / 16383.0f), 3.0f)); + return; + TCMC_SET_PAN: + tsf_channel_set_pan(f, channel, c->midiPan / 16383.0f); + return; + TCMC_SET_DATA: + if (c->midiRPN == 0) tsf_channel_set_pitchrange(f, channel, (c->midiData >> 8) + 0.01f * (c->midiData & 0x7F)); + else if (c->midiRPN == 1) tsf_channel_set_tuning(f, channel, (int)c->tuning + ((float)c->midiData - 8192.0f) / 8192.0f); //fine tune + else if (c->midiRPN == 2 && controller == 6) tsf_channel_set_tuning(f, channel, ((float)control_value - 64.0f) + (c->tuning - (int)c->tuning)); //coarse tune + return; + } + + TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel) + { + return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].presetIndex : 0); + } + + TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel) + { + return (f->channels && channel < f->channels->channelNum ? (f->channels->channels[channel].bank & 0x7FFF) : 0); + } + + TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel) + { + return (f->channels && channel < f->channels->channelNum ? f->presets[f->channels->channels[channel].presetIndex].preset : 0); + } + + TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel) + { + return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].pitchWheel : 8192); + } + + TSFDEF float tsf_channel_get_pan(tsf* f, int channel) + { + return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].panOffset - 0.5f : 0.5f); + } + + TSFDEF float tsf_channel_get_volume(tsf* f, int channel) + { + return (f->channels && channel < f->channels->channelNum ? tsf_decibelsToGain(f->channels->channels[channel].gainDB) : 1.0f); + } + +#ifdef __cplusplus +} +#endif + +#endif //TSF_IMPLEMENTATION \ No newline at end of file