diff --git a/polymer/eduke32/source/jaudiolib/Makefile b/polymer/eduke32/source/jaudiolib/Makefile index 57470b1f7..4ce711d53 100644 --- a/polymer/eduke32/source/jaudiolib/Makefile +++ b/polymer/eduke32/source/jaudiolib/Makefile @@ -46,6 +46,7 @@ OBJECTS=$(OBJ)/drivers.o \ $(OBJ)/formats.o \ $(OBJ)/vorbis.o \ $(OBJ)/flac.o \ + $(OBJ)/xa.o \ $(OBJ)/driver_nosound.o ifeq ($(PLATFORM),DARWIN) diff --git a/polymer/eduke32/source/jaudiolib/Makefile.msvc b/polymer/eduke32/source/jaudiolib/Makefile.msvc index fba518fca..9f60c5990 100644 --- a/polymer/eduke32/source/jaudiolib/Makefile.msvc +++ b/polymer/eduke32/source/jaudiolib/Makefile.msvc @@ -35,6 +35,7 @@ OBJECTS=$(OBJ)\drivers.o \ $(OBJ)\pitch.o \ $(OBJ)\formats.o \ $(OBJ)\vorbis.o \ + $(OBJ)\xa.o \ $(OBJ)\driver_nosound.o \ $(OBJ)\driver_directsound.o diff --git a/polymer/eduke32/source/jaudiolib/src/_multivc.h b/polymer/eduke32/source/jaudiolib/src/_multivc.h index 00413a0d0..f08f4813e 100644 --- a/polymer/eduke32/source/jaudiolib/src/_multivc.h +++ b/polymer/eduke32/source/jaudiolib/src/_multivc.h @@ -229,6 +229,7 @@ void MV_SetVoicePitch ( VoiceNode *voice, uint32_t rate, int32_t pitchoffset ); void MV_ReleaseVorbisVoice( VoiceNode * voice ); void MV_ReleaseFLACVoice( VoiceNode * voice ); +void MV_ReleaseXAVoice( VoiceNode * voice ); // implemented in mix.c void ClearBuffer_DW( void *ptr, unsigned data, int32_t length ); diff --git a/polymer/eduke32/source/jaudiolib/src/fx_man.c b/polymer/eduke32/source/jaudiolib/src/fx_man.c index 8f62b384a..febe90108 100644 --- a/polymer/eduke32/source/jaudiolib/src/fx_man.c +++ b/polymer/eduke32/source/jaudiolib/src/fx_man.c @@ -885,7 +885,15 @@ static wavedata FX_AutoDetectFormat(const char *ptr, uint32_t length) return VOC; break; case 'R'+('I'<<8)+('F'<<16)+('F'<<24): // RIFF - return WAV; + switch (LITTLE32(*(int32_t *)(ptr + 8))) + { + case 'C'+('D'<<8)+('X'<<16)+('A'<<24): // CDXA + return XA; + break; + default: + return WAV; + break; + } break; case 'O'+('g'<<8)+('g'<<16)+('S'<<24): // OggS return Vorbis; @@ -950,6 +958,9 @@ int32_t FX_PlayLoopedAuto(char *ptr, uint32_t length, int32_t loopstart, int32_t MV_Printf("FX_PlayLoopedAuto: FLAC support not included in this binary.\n"); #endif break; + case XA: + handle = MV_PlayXA(ptr, length, loopstart, loopend, pitchoffset, vol, left, right, priority, callbackval); + break; default: MV_Printf("FX_PlayLoopedAuto: Unknown or unsupported format.\n"); break; @@ -998,6 +1009,9 @@ int32_t FX_PlayAuto3D(char *ptr, uint32_t length, int32_t loophow, int32_t pitch MV_Printf("FX_PlayAuto3D: FLAC support not included in this binary.\n"); #endif break; + case XA: + handle = MV_PlayXA3D(ptr, length, loophow, pitchoffset, angle, distance, priority, callbackval); + break; default: MV_Printf("FX_PlayAuto3D: Unknown or unsupported format.\n"); break; diff --git a/polymer/eduke32/source/jaudiolib/src/multivoc.c b/polymer/eduke32/source/jaudiolib/src/multivoc.c index b5e57196b..a7295a647 100644 --- a/polymer/eduke32/source/jaudiolib/src/multivoc.c +++ b/polymer/eduke32/source/jaudiolib/src/multivoc.c @@ -183,6 +183,9 @@ const char *MV_ErrorString(int32_t ErrorNumber) case MV_InvalidFLACFile : return "Invalid FLAC file passed in to Multivoc."; + case MV_InvalidXAFile : + return "Invalid XA file passed in to Multivoc."; + case MV_InvalidMixMode : return "Invalid mix mode request in Multivoc."; @@ -310,6 +313,8 @@ static void MV_StopVoice(VoiceNode *voice) if (voice->wavetype == FLAC) MV_ReleaseFLACVoice(voice); #endif + if (voice->wavetype == XA) + MV_ReleaseXAVoice(voice); voice->handle = 0; } diff --git a/polymer/eduke32/source/jaudiolib/src/multivoc.h b/polymer/eduke32/source/jaudiolib/src/multivoc.h index 0c75e530b..5c47db3f2 100644 --- a/polymer/eduke32/source/jaudiolib/src/multivoc.h +++ b/polymer/eduke32/source/jaudiolib/src/multivoc.h @@ -86,6 +86,7 @@ typedef enum WAV, Vorbis, FLAC, + XA, } wavedata; #define MV_MINVOICEHANDLE 1 @@ -107,6 +108,7 @@ enum MV_Errors MV_InvalidWAVFile, MV_InvalidVorbisFile, MV_InvalidFLACFile, + MV_InvalidXAFile, MV_InvalidMixMode, MV_NullRecordFunction }; @@ -159,6 +161,11 @@ int32_t MV_PlayFLAC3D( char *ptr, uint32_t length, int32_t loophow, int32_t pi int32_t MV_PlayFLAC( char *ptr, uint32_t length, int32_t loopstart, int32_t loopend, int32_t pitchoffset, int32_t vol, int32_t left, int32_t right, int32_t priority, uint32_t callbackval ); +int32_t MV_PlayXA3D( char *ptr, uint32_t length, int32_t loophow, int32_t pitchoffset, int32_t angle, int32_t distance, + int32_t priority, uint32_t callbackval ); +int32_t MV_PlayXA( char *ptr, uint32_t length, int32_t loopstart, int32_t loopend, + int32_t pitchoffset, int32_t vol, int32_t left, int32_t right, int32_t priority, + uint32_t callbackval ); //void MV_CreateVolumeTable( int32_t index, int32_t volume, int32_t MaxVolume ); void MV_SetVolume( int32_t volume ); int32_t MV_GetVolume( void ); diff --git a/polymer/eduke32/source/jaudiolib/src/xa.c b/polymer/eduke32/source/jaudiolib/src/xa.c new file mode 100644 index 000000000..dbb6090d9 --- /dev/null +++ b/polymer/eduke32/source/jaudiolib/src/xa.c @@ -0,0 +1,516 @@ + +/** + * PlayStation XA (ADPCM) source support for MultiVoc + * Adapted and remixed from superxa2wav + */ + +#include +#include +#include +#include +#include +#include "pitch.h" +#include "multivoc.h" +#include "_multivc.h" + +//#define NO_XA_HEADER + +#define USE_FXD 1 + +#define kNumOfSamples 224 +#define kNumOfSGs 18 +#define TTYWidth 80 + +#ifndef max +# define max(a,b) ( ((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +# define min(a,b) ( ((a) < (b)) ? (a) : (b)) +#endif + +/* ADPCM */ +#define XA_DATA_START (0x44-48) + +#define FXD_FxdToPCM(dt) (max(min((short)((dt)>>16), 32767), -32768)) +#define DblToPCM(dt) (short)(max(min((dt), 32767), -32768)) + +#if USE_FXD +#define FXD_FxdToPcm16(dt) (max(min((dt)/2, 32767), -32768)) +#define FXD_Pcm16ToFxd(dt) ((int32_t)dt*2) +#endif + +typedef struct { + void * ptr; + size_t length; + size_t pos; + +#if USE_FXD + int32_t t1, t2; + int32_t t1_x, t2_x; +#else + double t1, t2; + double t1_x, t2_x; +#endif + + char *block; + size_t blocksize; + + VoiceNode *owner; +} xa_data; + +typedef char SoundGroup[128]; + +typedef struct XASector { + char sectorFiller[48]; + SoundGroup SoundGroups[18]; +} XASector; + +#if USE_FXD +static int32_t K0[4] = { + 0x00000000, + 0x0000F000, + 0x0001CC00, + 0x00018800, +}; +static int32_t K1[4] = { + 0x00000000, + 0x00000000, + -53248, // 0xFFFF3000, + -56320, // 0xFFFF2400, +}; +#else +static double K0[4] = { + 0.0, + 0.9375, + 1.796875, + 1.53125 +}; +static double K1[4] = { + 0.0, + 0.0, + -0.8125, + -0.859375 +}; +#endif + + +#if USE_FXD +static int32_t FXD_FixMul(int32_t a, int32_t b) +{ + int32_t high_a, low_a, high_b, low_b; + int32_t hahb, halb, lahb; + uint32_t lalb; + int32_t ret; + + high_a = a >> 16; + low_a = a & 0x0000FFFF; + high_b = b >> 16; + low_b = b & 0x0000FFFF; + + hahb = (high_a * high_b) << 16; + halb = high_a * low_b; + lahb = low_a * high_b; + lalb = (uint32_t)(low_a * low_b) >> 16; + + ret = hahb + halb + lahb + lalb; + + return ret; +} +#endif + + + +static int8_t getSoundData(char *buf, int32_t unit, int32_t sample) +{ + int8_t ret; + char *p; + int32_t offset, shift; + + p = buf; + shift = (unit%2) * 4; + + offset = 16 + (unit / 2) + (sample * 4); + p += offset; + + ret = (*p >> shift) & 0x0F; + + if (ret > 7) { + ret -= 16; + } + return ret; +} + +static int8_t getFilter(char *buf, int32_t unit) +{ + return (*(buf + 4 + unit) >> 4) & 0x03; +} + + +static int8_t getRange(char *buf, int32_t unit) +{ + return *(buf + 4 + unit) & 0x0F; +} + + +static void decodeSoundSectMono(XASector *ssct, xa_data * xad) +{ + size_t count = 0; + int8_t snddat, filt, range; + short decoded; + int32_t unit, sample; + int32_t sndgrp; +#if USE_FXD + int32_t tmp2, tmp3, tmp4, tmp5; +#else + double tmp2, tmp3, tmp4, tmp5; +#endif + char decodeBuf[kNumOfSGs*kNumOfSamples*2]; + + for (sndgrp = 0; sndgrp < kNumOfSGs; sndgrp++) + { + for (unit = 0; unit < 8; unit++) + { + range = getRange(ssct->SoundGroups[sndgrp], unit); + filt = getFilter(ssct->SoundGroups[sndgrp], unit); + for (sample = 0; sample < 28; sample++) + { + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit, sample); +#if USE_FXD + tmp2 = (int32_t)(snddat) << (12 - range); + tmp3 = FXD_Pcm16ToFxd(tmp2); + tmp4 = FXD_FixMul(K0[filt], xad->t1); + tmp5 = FXD_FixMul(K1[filt], xad->t2); + xad->t2 = xad->t1; + xad->t1 = tmp3 + tmp4 + tmp5; + decoded = FXD_FxdToPcm16(xad->t1); +#else + tmp2 = (double)(1 << (12 - range)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1 * K0[filt]; + tmp5 = xad->t2 * K1[filt]; + xad->t2 = xad->t1; + xad->t1 = tmp3 + tmp4 + tmp5; + decoded = DblToPCM(xad->t1); +#endif + decodeBuf[count++] = (char)(decoded & 0x0000ffff); + decodeBuf[count++] = (char)(decoded >> 8); + } + } + } + + if (count > xad->blocksize) + xad->block = (char*)realloc(xad->block, count); + + memcpy(xad->block, decodeBuf, count); + xad->blocksize = count; +} + +static void decodeSoundSectStereo(XASector *ssct, xa_data * xad) +{ + size_t count = 0; + int8_t snddat, filt, range; + int8_t filt1, range1; + short decoded; + int32_t unit, sample; + int32_t sndgrp; +#if USE_FXD + int32_t tmp2, tmp3, tmp4, tmp5; +#else + double tmp2, tmp3, tmp4, tmp5; +#endif + char decodeBuf[kNumOfSGs*kNumOfSamples*2]; + + for (sndgrp = 0; sndgrp < kNumOfSGs; sndgrp++) + { + for (unit = 0; unit < 8; unit+= 2) + { + range = getRange(ssct->SoundGroups[sndgrp], unit); + filt = getFilter(ssct->SoundGroups[sndgrp], unit); + range1 = getRange(ssct->SoundGroups[sndgrp], unit+1); + filt1 = getFilter(ssct->SoundGroups[sndgrp], unit+1); + + for (sample = 0; sample < 28; sample++) + { + // Channel 1 + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit, sample); +#if USE_FXD + tmp2 = (int32_t)(snddat) << (12 - range); + tmp3 = FXD_Pcm16ToFxd(tmp2); + tmp4 = FXD_FixMul(K0[filt], xad->t1); + tmp5 = FXD_FixMul(K1[filt], xad->t2); + xad->t2 = xad->t1; + xad->t1 = tmp3 + tmp4 + tmp5; + decoded = FXD_FxdToPcm16(xad->t1); +#else + tmp2 = (double)(1 << (12 - range)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1 * K0[filt]; + tmp5 = xad->t2 * K1[filt]; + xad->t2 = xad->t1; + xad->t1 = tmp3 + tmp4 + tmp5; + decoded = DblToPCM(xad->t1); +#endif + decodeBuf[count++] = (char)(decoded & 0x0000ffff); + decodeBuf[count++] = (char)(decoded >> 8); + + // Channel 2 + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit+1, sample); +#if USE_FXD + tmp2 = (int32_t)(snddat) << (12 - range1); + tmp3 = FXD_Pcm16ToFxd(tmp2); + tmp4 = FXD_FixMul(K0[filt1], xad->t1_x); + tmp5 = FXD_FixMul(K1[filt1], xad->t2_x); + xad->t2_x = xad->t1_x; + xad->t1_x = tmp3 + tmp4 + tmp5; + decoded = FXD_FxdToPcm16(xad->t1_x); +#else + tmp2 = (double)(1 << (12 - range1)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1_x * K0[filt1]; + tmp5 = xad->t2_x * K1[filt1]; + xad->t2_x = xad->t1_x; + xad->t1_x = tmp3 + tmp4 + tmp5; + decoded = DblToPCM(xad->t1_x); +#endif + decodeBuf[count++] = (char)(decoded & 0x0000ffff); + decodeBuf[count++] = (char)(decoded >> 8); + } + } + } + + if (count > xad->blocksize) + xad->block = (char*)realloc(xad->block, count); + + memcpy(xad->block, decodeBuf, count); + xad->blocksize = count; +} + + +/*--------------------------------------------------------------------- +Function: MV_GetNextXABlock + +Controls playback of XA data +---------------------------------------------------------------------*/ + +static playbackstatus MV_GetNextXABlock +( + VoiceNode *voice + ) + +{ + xa_data * xad = (xa_data *) voice->extra; + XASector ssct; + int coding; + + voice->Playing = TRUE; + + xad->t1 = xad->t2 = xad->t1_x = xad->t2_x = 0; + + do + { + size_t bytes = xad->length - xad->pos; + + if (sizeof(XASector) < bytes) + bytes = sizeof(XASector); + + memcpy(&ssct, (char*) xad->ptr + xad->pos, bytes); + xad->pos += bytes; + } +#define SUBMODE_REAL_TIME_SECTOR (1 << 6) +#define SUBMODE_FORM (1 << 5) +#define SUBMODE_AUDIO_DATA (1 << 2) + while (ssct.sectorFiller[46] != (SUBMODE_REAL_TIME_SECTOR | SUBMODE_FORM | SUBMODE_AUDIO_DATA)); + + coding = ssct.sectorFiller[47]; + + voice->channels = (coding & 3) + 1; + voice->SamplingRate = (((coding >> 2) & 3) == 1) ? 18900 : 37800; + + // CODEDUP multivoc.c MV_SetVoicePitch + voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate; + voice->FixedPointBufferSize = ( voice->RateScale * MV_MIXBUFFERSIZE ) - voice->RateScale; + MV_SetVoiceMixMode( voice ); + + if (voice->channels == 2) + decodeSoundSectStereo(&ssct, xad); + else + decodeSoundSectMono(&ssct, xad); + + voice->sound = xad->block; + voice->length = (((xad->blocksize * (voice->bits/8))/2)<bits) / 4; + voice->position = 0; + voice->BlockLength = 0; + + if (xad->length == xad->pos) + { + if (voice->LoopSize > 0) + xad->pos = XA_DATA_START; + else + { + voice->Playing = FALSE; + return NoMoreData; + } + } + + return KeepPlaying; +} + + +/*--------------------------------------------------------------------- +Function: MV_PlayXA3D + +Begin playback of sound data at specified angle and distance +from listener. +---------------------------------------------------------------------*/ + +int32_t MV_PlayXA3D +( + char *ptr, + uint32_t ptrlength, + int32_t loophow, + int32_t pitchoffset, + int32_t angle, + int32_t distance, + int32_t priority, + uint32_t callbackval + ) + +{ + int32_t left; + int32_t right; + int32_t mid; + int32_t volume; + int32_t status; + + if ( !MV_Installed ) + { + MV_SetErrorCode( MV_NotInstalled ); + return MV_Error; + } + + if ( distance < 0 ) + { + distance = -distance; + angle += MV_NUMPANPOSITIONS / 2; + } + + volume = MIX_VOLUME( distance ); + + // Ensure angle is within 0 - 127 + angle &= MV_MAXPANPOSITION; + + left = MV_PanTable[ angle ][ volume ].left; + right = MV_PanTable[ angle ][ volume ].right; + mid = max( 0, 255 - distance ); + + status = MV_PlayXA(ptr, ptrlength, pitchoffset, loophow, -1, mid, left, right, priority, callbackval); + + return status; +} + + +/*--------------------------------------------------------------------- +Function: MV_PlayXA + +Begin playback of sound data with the given sound levels and +priority. +---------------------------------------------------------------------*/ + +int32_t MV_PlayXA +( + char *ptr, + uint32_t ptrlength, + int32_t loopstart, + int32_t loopend, + int32_t pitchoffset, + int32_t vol, + int32_t left, + int32_t right, + int32_t priority, + uint32_t callbackval + ) + +{ + VoiceNode *voice; + xa_data * xad = 0; + + UNREFERENCED_PARAMETER(loopend); + + if ( !MV_Installed ) + { + MV_SetErrorCode( MV_NotInstalled ); + return MV_Error; + } + + xad = (xa_data *) malloc( sizeof(xa_data) ); + if (!xad) { + MV_SetErrorCode( MV_InvalidXAFile ); + return MV_Error; + } + + memset(xad, 0, sizeof(xa_data)); + xad->ptr = ptr; + xad->pos = XA_DATA_START; + xad->blocksize = 0; + xad->length = ptrlength; + + xad->block = NULL; + + // Request a voice from the voice pool + voice = MV_AllocVoice( priority ); + if ( voice == NULL ) + { + + free(xad); + MV_SetErrorCode( MV_NoVoices ); + return MV_Error; + } + + xad->owner = voice; + + voice->wavetype = XA; + voice->extra = (void*)xad; + voice->GetSound = MV_GetNextXABlock; + voice->NextBlock = xad->block; + voice->DemandFeed = NULL; + voice->LoopCount = 0; + voice->BlockLength = 0; + voice->PitchScale = PITCH_GetScale( pitchoffset ); + voice->next = NULL; + voice->prev = NULL; + voice->priority = priority; + voice->callbackval = callbackval; + + voice->bits = 16; + + voice->bits = 16; + + voice->Playing = TRUE; + voice->Paused = FALSE; + + voice->LoopStart = 0; + voice->LoopEnd = 0; + voice->LoopSize = (loopstart >= 0 ? 1 : 0); + + MV_SetVoiceVolume( voice, vol, left, right ); + MV_PlayVoice( voice ); + + return voice->handle; +} + + +void MV_ReleaseXAVoice( VoiceNode * voice ) +{ + xa_data * xad = (xa_data *) voice->extra; + + if (voice->wavetype != XA) { + return; + } + + if (xad->block != NULL) + free(xad->block); + free(xad); + + voice->extra = 0; +}