From 0b42254e4bce4daae03e1de0967cafde2c5c4359 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Sat, 8 Mar 2014 20:09:30 -0800 Subject: [PATCH] Updated playptmod, and now playptmod is unclipped and supports indefinite looping --- Frameworks/playptmod/playptmod/playptmod.c | 251 +++++++++++---------- Frameworks/playptmod/playptmod/playptmod.h | 3 +- Plugins/playptmod/playptmod/ptmodDecoder.m | 43 +++- 3 files changed, 161 insertions(+), 136 deletions(-) diff --git a/Frameworks/playptmod/playptmod/playptmod.c b/Frameworks/playptmod/playptmod/playptmod.c index e79ba8b88..b75a64433 100644 --- a/Frameworks/playptmod/playptmod/playptmod.c +++ b/Frameworks/playptmod/playptmod/playptmod.c @@ -1,5 +1,5 @@ /* -** - --=playptmod v1.05+ - 8bitbubsy 2010-2013=-- - +** - --=playptmod v1.10d - 8bitbubsy 2010-2014=-- - ** This is the native Win32 API version, no DLL needed in you ** production zip/rar whatever. ** @@ -62,6 +62,8 @@ ** */ +#define _USE_MATH_DEFINES // visual studio + #include "playptmod.h" #include "blip_buf.h" @@ -70,16 +72,12 @@ #include // malloc(), calloc(), free() #include // floorf(), sinf() -#ifndef M_PI -#define M_PI 3.141592653589793 -#endif - #define HI_NYBBLE(x) ((x) >> 4) #define LO_NYBBLE(x) ((x) & 0x0F) #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) #define DENORMAL_OFFSET 1E-10f -#define PT_MIN_PERIOD 108 -#define PT_MAX_PERIOD 907 +#define PT_MIN_PERIOD 78 +#define PT_MAX_PERIOD 937 #define MAX_CHANNELS 32 #define MOD_ROWS 64 #define MOD_SAMPLES 31 @@ -211,14 +209,12 @@ typedef struct typedef struct paula_filter_state { float LED[4]; - float high[2]; } Filter; typedef struct paula_filter_coefficients { float LED; float LEDFb; - float high; } FilterC; typedef struct voice_data @@ -282,7 +278,6 @@ typedef struct float *frequencyTable; float *extendedFrequencyTable; unsigned char *sinusTable; - int *panTable; int minPeriod; int maxPeriod; int loopCounter; @@ -384,7 +379,7 @@ static float calcRcCoeff(float sampleRate, float cutOffFreq) if (cutOffFreq >= (sampleRate / 2.0f)) return (1.0f); - return ((2.0f * 3.141592f) * cutOffFreq / sampleRate); + return (2.0f * (float)(M_PI) * cutOffFreq / sampleRate); } static BUF *bufopen(const unsigned char *bufToCopy, unsigned long bufferSize) @@ -445,49 +440,28 @@ static void bufseek(BUF *_SrcBuf, long _Offset, int _Origin) static inline int periodToNote(player *p, char finetune, short period) { - char l; - char m; - char h; + unsigned char i; + unsigned char maxNotes; short *tablePointer; if (p->minPeriod == PT_MIN_PERIOD) { - l = 0; - h = 35; - + maxNotes = 36; tablePointer = (short *)&rawAmigaPeriods[finetune * 37]; - while (h >= l) - { - m = (h + l) / 2; - - if (tablePointer[m] == period) - break; - else if (tablePointer[m] > period) - l = m + 1; - else - h = m - 1; - } } else { - l = 0; - h = 83; - + maxNotes = 84; tablePointer = (short *)&extendedRawPeriods[finetune * 85]; - while (h >= l) - { - m = (h + l) / 2; - - if (tablePointer[m] == period) - break; - else if (tablePointer[m] > period) - l = m + 1; - else - h = m - 1; - } } - return (m); + for (i = 0; i < maxNotes; ++i) + { + if (period >= tablePointer[i]) + break; + } + + return i; } static void mixerSwapChSource(player *p, int ch, const char *src, int length, int loopStart, int loopLength, int step) @@ -496,7 +470,7 @@ static void mixerSwapChSource(player *p, int ch, const char *src, int length, in p->v[ch].newData = src; p->v[ch].newLength = length; p->v[ch].newLoopLength = loopLength; - p->v[ch].newLoopEnd = loopLength + loopStart; + p->v[ch].newLoopEnd = loopStart + loopLength; p->v[ch].newStep = step; } @@ -511,24 +485,47 @@ static void mixerSetChSource(player *p, int ch, const char *src, int length, int p->v[ch].loopLength = loopLength; p->v[ch].step = step; - if (p->v[ch].index > 0) + if (offset > 0) { - if (p->v[ch].loopLength > 2) + if (loopLength > (2 * step)) { - if (p->v[ch].index >= p->v[ch].loopEnd) + if (offset >= p->v[ch].loopEnd) p->v[ch].index = 0; } - else if (p->v[ch].index >= p->v[ch].length) + else if (offset >= length) { p->v[ch].data = NULL; } } } +// adejr: these sin/cos approximations both use a 0..1 +// parameter range and have 'normalized' (1/2 = 0db) coefficients +// +// the coefficients are for LERP(x, x * x, 0.224) * sqrt(2) +// max_error is minimized with 0.224 = 0.0013012886 + +static inline float sinApx(float x) +{ + x = x * (2.0f - x); + return (x * 1.09742972f + x * x * 0.31678383f); +} + +static inline float cosApx(float x) +{ + x = (1.0f - x) * (1.0f + x); + return (x * 1.09742972f + x * x * 0.31678383f); +} + static void mixerSetChPan(player *p, int ch, int pan) { - p->v[ch].panL = p->panTable[256 - pan]; - p->v[ch].panR = p->panTable[pan]; + float newpan; + if (pan == 255) pan = 256; // 100% R pan fix for 8-bit pan + + newpan = pan * (1.0f / 256.0f); + + p->v[ch].panL = (int)(256.0f * cosApx(newpan)); + p->v[ch].panR = (int)(256.0f * sinApx(newpan)); } static void mixerSetChVol(player *p, int ch, int vol) @@ -572,9 +569,9 @@ static void mixerSetChRate(player *p, int ch, float rate) p->v[ch].rate = rate; } -static void outputAudio(player *p, short *target, int numSamples) +static void outputAudio(player *p, int *target, int numSamples) { - short *out; + int *out; int i; int j; int step; @@ -651,17 +648,18 @@ static void outputAudio(player *p, short *target, int numSamples) if (p->v[i].newLoopLength <= (2 * step)) { p->v[i].data = NULL; - continue; } - + + p->v[i].index = p->v[i].loopEnd - p->v[i].loopLength; + p->v[i].data = p->v[i].newData; p->v[i].length = p->v[i].newLength; p->v[i].loopEnd = p->v[i].newLoopEnd; p->v[i].loopLength = p->v[i].newLoopLength; + p->v[i].frac = 0.0f; + step = p->v[i].step = p->v[i].newStep; - - p->v[i].index = p->v[i].loopEnd - p->v[i].loopLength; } else { @@ -678,13 +676,17 @@ static void outputAudio(player *p, short *target, int numSamples) if (p->v[i].newLoopLength <= 2) { p->v[i].data = NULL; - break; + continue; } + p->v[i].index = 0; + p->v[i].data = p->v[i].newData; p->v[i].length = p->v[i].newLength; p->v[i].loopEnd = p->v[i].newLoopEnd; p->v[i].loopLength = p->v[i].newLoopLength; + p->v[i].frac = 0.0f; + step = p->v[i].step = p->v[i].newStep; } else @@ -695,15 +697,6 @@ static void outputAudio(player *p, short *target, int numSamples) } } } - else if (p->v[i].swapSampleFlag == true) - { - p->v[i].swapSampleFlag = false; - p->v[i].data = p->v[i].newData; - p->v[i].length = p->v[i].newLength; - p->v[i].loopEnd = p->v[i].newLoopEnd; - p->v[i].loopLength = p->v[i].newLoopLength; - p->v[i].step = p->v[i].newStep; - } if ((j < numSamples) && (p->v[i].data == NULL)) { @@ -732,9 +725,9 @@ static void outputAudio(player *p, short *target, int numSamples) float downscale; if (p->numChans <= 4) - downscale = 1.0f / (96.0f * 256.0f); + downscale = 1.0f / 192.0f; else - downscale = 1.0f / (160.0f * 256.0f); + downscale = 1.0f / 256.0f; for (i = 0; i < numSamples; ++i) { @@ -754,22 +747,16 @@ static void outputAudio(player *p, short *target, int numSamples) R = p->filter.LED[3]; } - L -= p->filter.high[0]; - R -= p->filter.high[1]; - - p->filter.high[0] += (p->filterC.high * L + DENORMAL_OFFSET); - p->filter.high[1] += (p->filterC.high * R + DENORMAL_OFFSET); - L *= downscale; R *= downscale; - L = CLAMP(L, -32768.0f, 32767.0f); - R = CLAMP(R, -32768.0f, 32767.0f); + L = CLAMP(L, INT_MIN, INT_MAX); + R = CLAMP(R, INT_MIN, INT_MAX); if ( out ) { - *out++ = (short)(int)(L); - *out++ = (short)(int)(R); + *out++ = (int)(L); + *out++ = (int)(R); } } } @@ -828,8 +815,11 @@ static int playptmod_LoadMTM(player *p, BUF *fmodule) { if (p->source->head.pan[i] <= 15) { - p->source->head.pan[i] -= (p->source->head.pan[i] & 8) / 8; - p->source->head.pan[i] = (((int)p->source->head.pan[i]) * 255) / 14; + // 8bitbubsy: WTF no, just do << 4 then if (p == 255) p = 256 in mixer + //p->source->head.pan[i] -= (p->source->head.pan[i] & 8) / 8; + //p->source->head.pan[i] = (((int)p->source->head.pan[i]) * 255) / 14; + + p->source->head.pan[i] <<= 4; p->source->head.volume[i] = 64; } else @@ -1311,7 +1301,6 @@ int playptmod_LoadMem(void *_p, const unsigned char *buf, unsigned long bufLengt else if (note->param & 0x0F) { note->command = 0x01; - note->param &= 0x0F; } } } @@ -1771,28 +1760,12 @@ static void efxTremoloControl(player *p, mod_channel *ch) static void efxKarplusStrong(player *p, mod_channel *ch) { - char *sampleLoopData; - unsigned int loopLength; - unsigned int loopLengthCounter; - MODULE_SAMPLE *s; + // WTF ! + // KarplusStrong sucks, since some MODs + // used E8x for other effects instead. - if (ch->sample > 0) - { - s = &p->source->samples[ch->sample - 1]; + (void)ch; - sampleLoopData = p->source->sampleData + s->offset + s->loopStart; - - loopLength = s->loopLength - 2; - loopLengthCounter = loopLength; - - while (loopLengthCounter--) - { - *sampleLoopData = (*sampleLoopData + *(sampleLoopData + 1)) / 2; - sampleLoopData++; - } - - *sampleLoopData = (*sampleLoopData + *(sampleLoopData - loopLength)) / 2; - } } static void efxRetrigNote(player *p, mod_channel *ch) @@ -2274,6 +2247,7 @@ static void fxSampleOffset(player *p, mod_channel *ch) ch->offsetTemp = ch->param * 256; ch->offset += ch->offsetTemp; + ch->offsetBugNotAdded = false; } } @@ -2414,13 +2388,13 @@ static void processEffects(player *p, mod_channel *ch) static void fxPan(player *p, mod_channel *ch) { if (p->modTick == 0) - mixerSetChPan(p, ch->chanIndex, (int)((float)ch->param * (256.0f / 255.0f))); + mixerSetChPan(p, ch->chanIndex, ch->param); } static void efxPan(player *p, mod_channel *ch) { if (p->modTick == 0) - mixerSetChPan(p, ch->chanIndex, (int)((float)LO_NYBBLE(ch->param) * (256.0f / 15.0f))); + mixerSetChPan(p, ch->chanIndex, LO_NYBBLE(ch->param) << 4); } void playptmod_Stop(void *_p) @@ -2483,11 +2457,18 @@ static void fetchPatternData(player *p, mod_channel *ch) ch->fineTune = LO_NYBBLE(ch->param); } - tempNote = periodToNote(p, 0, note->period); - - ch->noNote = false; - ch->tempPeriod = (p->minPeriod == PT_MIN_PERIOD) ? rawAmigaPeriods[(ch->fineTune * 37) + tempNote] : extendedRawPeriods[(ch->fineTune * 85) + tempNote]; - ch->flags |= FLAG_NOTE; + tempNote = periodToNote(p, 0, note->period); + if ((p->minPeriod == PT_MIN_PERIOD) && (tempNote == 36)) // PT/NT/STK/UST only + { + ch->noNote = true; + mixerSetChSource(p, ch->chanIndex, NULL, 0, 0, 0, 0, 0); + } + else + { + ch->noNote = false; + ch->tempPeriod = (p->minPeriod == PT_MIN_PERIOD) ? rawAmigaPeriods[(ch->fineTune * 37) + tempNote] : extendedRawPeriods[(ch->fineTune * 85) + tempNote]; + ch->flags |= FLAG_NOTE; + } } else { @@ -2722,7 +2703,7 @@ static int pulsateSamples(player *p, int samples) return samples; } -void playptmod_Render(void *_p, short *target, int length) +void playptmod_Render(void *_p, int *target, int length) { player *p = (player *)_p; @@ -2742,6 +2723,39 @@ void playptmod_Render(void *_p, short *target, int length) } } +void playptmod_Render16(void *_p, short *target, int length) +{ + player *p = (player *)_p; + + int tempBuffer[512]; + int * temp = ( target ) ? tempBuffer : 0; + + if (p->modulePlaying == true) + { + static const int soundBufferSamples = soundBufferSize / 4; + + while (length) + { + int i, tempSamples = CLAMP(length, 0, soundBufferSamples); + tempSamples = CLAMP(length, 0, 256); + tempSamples = pulsateSamples(p, tempSamples); + length -= tempSamples; + + outputAudio(p, temp, tempSamples); + + if ( target ) + for (i = 0; i < tempSamples * 2; ++i) + { + int s = tempBuffer[ i ] >> 8; + s = CLAMP(s, -32768, 32767); + target[ i ] = (short)s; + } + + if ( target ) target += (tempSamples * 2); + } + } +} + void *playptmod_Create(int samplingFrequency) { player *p = (player *) calloc(1, sizeof(player)); @@ -2755,8 +2769,10 @@ void *playptmod_Create(int samplingFrequency) for (i = 0; i < 32; ++i) p->sinusTable[i] = (unsigned char)floorf(255.0f * sinf(((float)i * 3.141592f) / 32.0f)); - p->frequencyTable = (float *)malloc(sizeof (float) * 908); - for (i = 108; i <= 907; ++i) // 0..107 will never be looked up, junk is OK + p->frequencyTable = (float *)malloc(sizeof (float) * 938); + + p->frequencyTable[0] = (float)samplingFrequency / 7093790.0f; + for (i = 1; i <= 937; ++i) p->frequencyTable[i] = (float)samplingFrequency / (7093790.0f / (2.0f * (float)i)); for (j = 0; j < 16; ++j) @@ -2772,21 +2788,11 @@ void *playptmod_Create(int samplingFrequency) for (i = 14; i <= 1712; ++i) // 0..14 will never be looked up, junk is OK p->extendedFrequencyTable[i] = (float)samplingFrequency / (7093790.0f / (2.0f * (float)i)); - p->panTable = (int *)malloc(sizeof (int) * 257); - norm = 1.0f / sinf(1.0f / (M_PI / 2)); - for (i = 0; i <= 256; ++i) - { - float pan = (float)i * 1.0f / 256.0f; - - // -3dB pan law ( pan = sin x/half_pi ) - p->panTable[i] = 256.0f * sinf((pan) / (M_PI / 2)) * norm; - } p->mixBufferL = (float *)malloc(soundBufferSize * sizeof (float)); p->mixBufferR = (float *)malloc(soundBufferSize * sizeof (float)); p->filterC.LED = calcRcCoeff((float)samplingFrequency, 3090.0f); p->filterC.LEDFb = 0.125f + 0.125f / (1.0f - p->filterC.LED); - p->filterC.high = calcRcCoeff((float)p->soundFrequency, 5.2f); p->useLEDFilter = false; @@ -2883,8 +2889,7 @@ void playptmod_Free(void *_p) } free(p->mixBufferL); - free(p->mixBufferR); - free(p->panTable); + free(p->mixBufferR);; free(p->sinusTable); free(p->frequencyTable); free(p->extendedFrequencyTable); diff --git a/Frameworks/playptmod/playptmod/playptmod.h b/Frameworks/playptmod/playptmod/playptmod.h index 6cdb872b8..4cc7117ef 100644 --- a/Frameworks/playptmod/playptmod/playptmod.h +++ b/Frameworks/playptmod/playptmod/playptmod.h @@ -23,7 +23,8 @@ int playptmod_Load(void *p, const char *filename); void playptmod_Play(void *p, unsigned int startOrder); void playptmod_Stop(void *p); -void playptmod_Render(void *p, signed short *target, int length); +void playptmod_Render(void *p, signed int *target, int length); +void playptmod_Render16(void *p, signed short *target, int length); void playptmod_Mute(void *p, int channel, int mute); diff --git a/Plugins/playptmod/playptmod/ptmodDecoder.m b/Plugins/playptmod/playptmod/ptmodDecoder.m index 06d89b825..01d1db892 100755 --- a/Plugins/playptmod/playptmod/ptmodDecoder.m +++ b/Plugins/playptmod/playptmod/ptmodDecoder.m @@ -10,6 +10,8 @@ #import "Logging.h" +#import "PlaylistController.h" + @implementation ptmodDecoder BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, int test_vblank, const void * src, unsigned long size, unsigned int subsong ) @@ -135,9 +137,9 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in [NSNumber numberWithInt:0], @"bitrate", [NSNumber numberWithFloat:44100], @"sampleRate", [NSNumber numberWithDouble:totalFrames], @"totalFrames", - [NSNumber numberWithInt:16], @"bitsPerSample", //Samples are short - [NSNumber numberWithBool:NO], @"floatingPoint", - [NSNumber numberWithInt:2], @"channels", //output from gme_play is in stereo + [NSNumber numberWithInt:32], @"bitsPerSample", + [NSNumber numberWithBool:YES], @"floatingPoint", + [NSNumber numberWithInt:2], @"channels", [NSNumber numberWithBool:YES], @"seekable", @"host", @"endian", nil]; @@ -145,7 +147,9 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in - (int)readAudio:(void *)buf frames:(UInt32)frames { - if ( framesRead >= totalFrames ) + BOOL repeat_one = IsRepeatOneSet(); + + if ( !repeat_one && framesRead >= totalFrames ) return 0; if ( !ptmod ) @@ -158,27 +162,27 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in while ( total < frames ) { int framesToRender = 512; if ( framesToRender > totalFrames - framesRead ) - framesToRender = totalFrames - framesRead; + framesToRender = (int)(totalFrames - framesRead); if ( framesToRender > frames - total ) framesToRender = frames - total; - int16_t * sampleBuf = ( int16_t * ) buf + total * 2; + int32_t * sampleBuf = ( int32_t * ) buf + total * 2; playptmod_Render( ptmod, sampleBuf, framesToRender ); - if ( framesRead + framesToRender > framesLength ) { + if ( !repeat_one && framesRead + framesToRender > framesLength ) { long fadeStart = ( framesLength > framesRead ) ? framesLength : framesRead; long fadeEnd = ( framesRead + framesToRender < totalFrames ) ? framesRead + framesToRender : totalFrames; const long fadeTotal = totalFrames - framesLength; for ( long fadePos = fadeStart; fadePos < fadeEnd; ++fadePos ) { const long scale = ( fadeTotal - ( fadePos - framesLength ) ); const long offset = fadePos - framesRead; - int16_t * samples = sampleBuf + offset * 2; - samples[ 0 ] = samples[ 0 ] * scale / fadeTotal; - samples[ 1 ] = samples[ 1 ] * scale / fadeTotal; + int32_t * samples = sampleBuf + offset * 2; + samples[ 0 ] = (int32_t)(samples[ 0 ] * scale / fadeTotal); + samples[ 1 ] = (int32_t)(samples[ 1 ] * scale / fadeTotal); } - framesToRender = fadeEnd - framesRead; + framesToRender = (int)(fadeEnd - framesRead); } if ( !framesToRender ) @@ -188,6 +192,14 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in framesRead += framesToRender; } + for ( int i = 0; i < total; ++i ) + { + int32_t * sampleIn = ( int32_t * ) buf + i * 2; + float * sampleOut = ( float * ) buf + i * 2; + sampleOut[ 0 ] = sampleIn[ 0 ] * (1.0f / 16777216.0f); + sampleOut[ 1 ] = sampleIn[ 1 ] * (1.0f / 16777216.0f); + } + return total; } @@ -200,7 +212,14 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in return 0; } - playptmod_Render( ptmod, NULL, frame - framesRead ); + while ( framesRead < frame ) + { + int frames_todo = INT_MAX; + if ( frames_todo > frame - framesRead ) + frames_todo = (int)( frame - framesRead ); + playptmod_Render( ptmod, NULL, frames_todo ); + framesRead += frames_todo; + } framesRead = frame;