Updated playptmod, and now playptmod is unclipped and supports indefinite looping
parent
ec40c5041c
commit
0b42254e4b
|
@ -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
|
** This is the native Win32 API version, no DLL needed in you
|
||||||
** production zip/rar whatever.
|
** production zip/rar whatever.
|
||||||
**
|
**
|
||||||
|
@ -62,6 +62,8 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES // visual studio
|
||||||
|
|
||||||
#include "playptmod.h"
|
#include "playptmod.h"
|
||||||
#include "blip_buf.h"
|
#include "blip_buf.h"
|
||||||
|
|
||||||
|
@ -70,16 +72,12 @@
|
||||||
#include <stdlib.h> // malloc(), calloc(), free()
|
#include <stdlib.h> // malloc(), calloc(), free()
|
||||||
#include <math.h> // floorf(), sinf()
|
#include <math.h> // floorf(), sinf()
|
||||||
|
|
||||||
#ifndef M_PI
|
|
||||||
#define M_PI 3.141592653589793
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HI_NYBBLE(x) ((x) >> 4)
|
#define HI_NYBBLE(x) ((x) >> 4)
|
||||||
#define LO_NYBBLE(x) ((x) & 0x0F)
|
#define LO_NYBBLE(x) ((x) & 0x0F)
|
||||||
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
||||||
#define DENORMAL_OFFSET 1E-10f
|
#define DENORMAL_OFFSET 1E-10f
|
||||||
#define PT_MIN_PERIOD 108
|
#define PT_MIN_PERIOD 78
|
||||||
#define PT_MAX_PERIOD 907
|
#define PT_MAX_PERIOD 937
|
||||||
#define MAX_CHANNELS 32
|
#define MAX_CHANNELS 32
|
||||||
#define MOD_ROWS 64
|
#define MOD_ROWS 64
|
||||||
#define MOD_SAMPLES 31
|
#define MOD_SAMPLES 31
|
||||||
|
@ -211,14 +209,12 @@ typedef struct
|
||||||
typedef struct paula_filter_state
|
typedef struct paula_filter_state
|
||||||
{
|
{
|
||||||
float LED[4];
|
float LED[4];
|
||||||
float high[2];
|
|
||||||
} Filter;
|
} Filter;
|
||||||
|
|
||||||
typedef struct paula_filter_coefficients
|
typedef struct paula_filter_coefficients
|
||||||
{
|
{
|
||||||
float LED;
|
float LED;
|
||||||
float LEDFb;
|
float LEDFb;
|
||||||
float high;
|
|
||||||
} FilterC;
|
} FilterC;
|
||||||
|
|
||||||
typedef struct voice_data
|
typedef struct voice_data
|
||||||
|
@ -282,7 +278,6 @@ typedef struct
|
||||||
float *frequencyTable;
|
float *frequencyTable;
|
||||||
float *extendedFrequencyTable;
|
float *extendedFrequencyTable;
|
||||||
unsigned char *sinusTable;
|
unsigned char *sinusTable;
|
||||||
int *panTable;
|
|
||||||
int minPeriod;
|
int minPeriod;
|
||||||
int maxPeriod;
|
int maxPeriod;
|
||||||
int loopCounter;
|
int loopCounter;
|
||||||
|
@ -384,7 +379,7 @@ static float calcRcCoeff(float sampleRate, float cutOffFreq)
|
||||||
if (cutOffFreq >= (sampleRate / 2.0f))
|
if (cutOffFreq >= (sampleRate / 2.0f))
|
||||||
return (1.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)
|
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)
|
static inline int periodToNote(player *p, char finetune, short period)
|
||||||
{
|
{
|
||||||
char l;
|
unsigned char i;
|
||||||
char m;
|
unsigned char maxNotes;
|
||||||
char h;
|
|
||||||
short *tablePointer;
|
short *tablePointer;
|
||||||
|
|
||||||
if (p->minPeriod == PT_MIN_PERIOD)
|
if (p->minPeriod == PT_MIN_PERIOD)
|
||||||
{
|
{
|
||||||
l = 0;
|
maxNotes = 36;
|
||||||
h = 35;
|
|
||||||
|
|
||||||
tablePointer = (short *)&rawAmigaPeriods[finetune * 37];
|
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
|
else
|
||||||
{
|
{
|
||||||
l = 0;
|
maxNotes = 84;
|
||||||
h = 83;
|
|
||||||
|
|
||||||
tablePointer = (short *)&extendedRawPeriods[finetune * 85];
|
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)
|
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].newData = src;
|
||||||
p->v[ch].newLength = length;
|
p->v[ch].newLength = length;
|
||||||
p->v[ch].newLoopLength = loopLength;
|
p->v[ch].newLoopLength = loopLength;
|
||||||
p->v[ch].newLoopEnd = loopLength + loopStart;
|
p->v[ch].newLoopEnd = loopStart + loopLength;
|
||||||
p->v[ch].newStep = step;
|
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].loopLength = loopLength;
|
||||||
p->v[ch].step = step;
|
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;
|
p->v[ch].index = 0;
|
||||||
}
|
}
|
||||||
else if (p->v[ch].index >= p->v[ch].length)
|
else if (offset >= length)
|
||||||
{
|
{
|
||||||
p->v[ch].data = NULL;
|
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)
|
static void mixerSetChPan(player *p, int ch, int pan)
|
||||||
{
|
{
|
||||||
p->v[ch].panL = p->panTable[256 - pan];
|
float newpan;
|
||||||
p->v[ch].panR = p->panTable[pan];
|
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)
|
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;
|
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 i;
|
||||||
int j;
|
int j;
|
||||||
int step;
|
int step;
|
||||||
|
@ -651,17 +648,18 @@ static void outputAudio(player *p, short *target, int numSamples)
|
||||||
if (p->v[i].newLoopLength <= (2 * step))
|
if (p->v[i].newLoopLength <= (2 * step))
|
||||||
{
|
{
|
||||||
p->v[i].data = NULL;
|
p->v[i].data = NULL;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->v[i].index = p->v[i].loopEnd - p->v[i].loopLength;
|
||||||
|
|
||||||
p->v[i].data = p->v[i].newData;
|
p->v[i].data = p->v[i].newData;
|
||||||
p->v[i].length = p->v[i].newLength;
|
p->v[i].length = p->v[i].newLength;
|
||||||
p->v[i].loopEnd = p->v[i].newLoopEnd;
|
p->v[i].loopEnd = p->v[i].newLoopEnd;
|
||||||
p->v[i].loopLength = p->v[i].newLoopLength;
|
p->v[i].loopLength = p->v[i].newLoopLength;
|
||||||
|
p->v[i].frac = 0.0f;
|
||||||
|
|
||||||
step = p->v[i].step = p->v[i].newStep;
|
step = p->v[i].step = p->v[i].newStep;
|
||||||
|
|
||||||
p->v[i].index = p->v[i].loopEnd - p->v[i].loopLength;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -678,13 +676,17 @@ static void outputAudio(player *p, short *target, int numSamples)
|
||||||
if (p->v[i].newLoopLength <= 2)
|
if (p->v[i].newLoopLength <= 2)
|
||||||
{
|
{
|
||||||
p->v[i].data = NULL;
|
p->v[i].data = NULL;
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->v[i].index = 0;
|
||||||
|
|
||||||
p->v[i].data = p->v[i].newData;
|
p->v[i].data = p->v[i].newData;
|
||||||
p->v[i].length = p->v[i].newLength;
|
p->v[i].length = p->v[i].newLength;
|
||||||
p->v[i].loopEnd = p->v[i].newLoopEnd;
|
p->v[i].loopEnd = p->v[i].newLoopEnd;
|
||||||
p->v[i].loopLength = p->v[i].newLoopLength;
|
p->v[i].loopLength = p->v[i].newLoopLength;
|
||||||
|
p->v[i].frac = 0.0f;
|
||||||
|
|
||||||
step = p->v[i].step = p->v[i].newStep;
|
step = p->v[i].step = p->v[i].newStep;
|
||||||
}
|
}
|
||||||
else
|
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))
|
if ((j < numSamples) && (p->v[i].data == NULL))
|
||||||
{
|
{
|
||||||
|
@ -732,9 +725,9 @@ static void outputAudio(player *p, short *target, int numSamples)
|
||||||
float downscale;
|
float downscale;
|
||||||
|
|
||||||
if (p->numChans <= 4)
|
if (p->numChans <= 4)
|
||||||
downscale = 1.0f / (96.0f * 256.0f);
|
downscale = 1.0f / 192.0f;
|
||||||
else
|
else
|
||||||
downscale = 1.0f / (160.0f * 256.0f);
|
downscale = 1.0f / 256.0f;
|
||||||
|
|
||||||
for (i = 0; i < numSamples; ++i)
|
for (i = 0; i < numSamples; ++i)
|
||||||
{
|
{
|
||||||
|
@ -754,22 +747,16 @@ static void outputAudio(player *p, short *target, int numSamples)
|
||||||
R = p->filter.LED[3];
|
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;
|
L *= downscale;
|
||||||
R *= downscale;
|
R *= downscale;
|
||||||
|
|
||||||
L = CLAMP(L, -32768.0f, 32767.0f);
|
L = CLAMP(L, INT_MIN, INT_MAX);
|
||||||
R = CLAMP(R, -32768.0f, 32767.0f);
|
R = CLAMP(R, INT_MIN, INT_MAX);
|
||||||
|
|
||||||
if ( out )
|
if ( out )
|
||||||
{
|
{
|
||||||
*out++ = (short)(int)(L);
|
*out++ = (int)(L);
|
||||||
*out++ = (short)(int)(R);
|
*out++ = (int)(R);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -828,8 +815,11 @@ static int playptmod_LoadMTM(player *p, BUF *fmodule)
|
||||||
{
|
{
|
||||||
if (p->source->head.pan[i] <= 15)
|
if (p->source->head.pan[i] <= 15)
|
||||||
{
|
{
|
||||||
p->source->head.pan[i] -= (p->source->head.pan[i] & 8) / 8;
|
// 8bitbubsy: WTF no, just do << 4 then if (p == 255) p = 256 in mixer
|
||||||
p->source->head.pan[i] = (((int)p->source->head.pan[i]) * 255) / 14;
|
//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;
|
p->source->head.volume[i] = 64;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1311,7 +1301,6 @@ int playptmod_LoadMem(void *_p, const unsigned char *buf, unsigned long bufLengt
|
||||||
else if (note->param & 0x0F)
|
else if (note->param & 0x0F)
|
||||||
{
|
{
|
||||||
note->command = 0x01;
|
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)
|
static void efxKarplusStrong(player *p, mod_channel *ch)
|
||||||
{
|
{
|
||||||
char *sampleLoopData;
|
// WTF !
|
||||||
unsigned int loopLength;
|
// KarplusStrong sucks, since some MODs
|
||||||
unsigned int loopLengthCounter;
|
// used E8x for other effects instead.
|
||||||
MODULE_SAMPLE *s;
|
|
||||||
|
|
||||||
if (ch->sample > 0)
|
(void)ch;
|
||||||
{
|
|
||||||
s = &p->source->samples[ch->sample - 1];
|
|
||||||
|
|
||||||
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)
|
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->offsetTemp = ch->param * 256;
|
||||||
|
|
||||||
ch->offset += ch->offsetTemp;
|
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)
|
static void fxPan(player *p, mod_channel *ch)
|
||||||
{
|
{
|
||||||
if (p->modTick == 0)
|
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)
|
static void efxPan(player *p, mod_channel *ch)
|
||||||
{
|
{
|
||||||
if (p->modTick == 0)
|
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)
|
void playptmod_Stop(void *_p)
|
||||||
|
@ -2483,11 +2457,18 @@ static void fetchPatternData(player *p, mod_channel *ch)
|
||||||
ch->fineTune = LO_NYBBLE(ch->param);
|
ch->fineTune = LO_NYBBLE(ch->param);
|
||||||
}
|
}
|
||||||
|
|
||||||
tempNote = periodToNote(p, 0, note->period);
|
tempNote = periodToNote(p, 0, note->period);
|
||||||
|
if ((p->minPeriod == PT_MIN_PERIOD) && (tempNote == 36)) // PT/NT/STK/UST only
|
||||||
ch->noNote = false;
|
{
|
||||||
ch->tempPeriod = (p->minPeriod == PT_MIN_PERIOD) ? rawAmigaPeriods[(ch->fineTune * 37) + tempNote] : extendedRawPeriods[(ch->fineTune * 85) + tempNote];
|
ch->noNote = true;
|
||||||
ch->flags |= FLAG_NOTE;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -2722,7 +2703,7 @@ static int pulsateSamples(player *p, int samples)
|
||||||
return 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;
|
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)
|
void *playptmod_Create(int samplingFrequency)
|
||||||
{
|
{
|
||||||
player *p = (player *) calloc(1, sizeof(player));
|
player *p = (player *) calloc(1, sizeof(player));
|
||||||
|
@ -2755,8 +2769,10 @@ void *playptmod_Create(int samplingFrequency)
|
||||||
for (i = 0; i < 32; ++i)
|
for (i = 0; i < 32; ++i)
|
||||||
p->sinusTable[i] = (unsigned char)floorf(255.0f * sinf(((float)i * 3.141592f) / 32.0f));
|
p->sinusTable[i] = (unsigned char)floorf(255.0f * sinf(((float)i * 3.141592f) / 32.0f));
|
||||||
|
|
||||||
p->frequencyTable = (float *)malloc(sizeof (float) * 908);
|
p->frequencyTable = (float *)malloc(sizeof (float) * 938);
|
||||||
for (i = 108; i <= 907; ++i) // 0..107 will never be looked up, junk is OK
|
|
||||||
|
p->frequencyTable[0] = (float)samplingFrequency / 7093790.0f;
|
||||||
|
for (i = 1; i <= 937; ++i)
|
||||||
p->frequencyTable[i] = (float)samplingFrequency / (7093790.0f / (2.0f * (float)i));
|
p->frequencyTable[i] = (float)samplingFrequency / (7093790.0f / (2.0f * (float)i));
|
||||||
|
|
||||||
for (j = 0; j < 16; ++j)
|
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
|
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->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->mixBufferL = (float *)malloc(soundBufferSize * sizeof (float));
|
||||||
p->mixBufferR = (float *)malloc(soundBufferSize * sizeof (float));
|
p->mixBufferR = (float *)malloc(soundBufferSize * sizeof (float));
|
||||||
|
|
||||||
p->filterC.LED = calcRcCoeff((float)samplingFrequency, 3090.0f);
|
p->filterC.LED = calcRcCoeff((float)samplingFrequency, 3090.0f);
|
||||||
p->filterC.LEDFb = 0.125f + 0.125f / (1.0f - p->filterC.LED);
|
p->filterC.LEDFb = 0.125f + 0.125f / (1.0f - p->filterC.LED);
|
||||||
p->filterC.high = calcRcCoeff((float)p->soundFrequency, 5.2f);
|
|
||||||
|
|
||||||
p->useLEDFilter = false;
|
p->useLEDFilter = false;
|
||||||
|
|
||||||
|
@ -2883,8 +2889,7 @@ void playptmod_Free(void *_p)
|
||||||
}
|
}
|
||||||
|
|
||||||
free(p->mixBufferL);
|
free(p->mixBufferL);
|
||||||
free(p->mixBufferR);
|
free(p->mixBufferR);;
|
||||||
free(p->panTable);
|
|
||||||
free(p->sinusTable);
|
free(p->sinusTable);
|
||||||
free(p->frequencyTable);
|
free(p->frequencyTable);
|
||||||
free(p->extendedFrequencyTable);
|
free(p->extendedFrequencyTable);
|
||||||
|
|
|
@ -23,7 +23,8 @@ int playptmod_Load(void *p, const char *filename);
|
||||||
|
|
||||||
void playptmod_Play(void *p, unsigned int startOrder);
|
void playptmod_Play(void *p, unsigned int startOrder);
|
||||||
void playptmod_Stop(void *p);
|
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);
|
void playptmod_Mute(void *p, int channel, int mute);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#import "Logging.h"
|
#import "Logging.h"
|
||||||
|
|
||||||
|
#import "PlaylistController.h"
|
||||||
|
|
||||||
@implementation ptmodDecoder
|
@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 )
|
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 numberWithInt:0], @"bitrate",
|
||||||
[NSNumber numberWithFloat:44100], @"sampleRate",
|
[NSNumber numberWithFloat:44100], @"sampleRate",
|
||||||
[NSNumber numberWithDouble:totalFrames], @"totalFrames",
|
[NSNumber numberWithDouble:totalFrames], @"totalFrames",
|
||||||
[NSNumber numberWithInt:16], @"bitsPerSample", //Samples are short
|
[NSNumber numberWithInt:32], @"bitsPerSample",
|
||||||
[NSNumber numberWithBool:NO], @"floatingPoint",
|
[NSNumber numberWithBool:YES], @"floatingPoint",
|
||||||
[NSNumber numberWithInt:2], @"channels", //output from gme_play is in stereo
|
[NSNumber numberWithInt:2], @"channels",
|
||||||
[NSNumber numberWithBool:YES], @"seekable",
|
[NSNumber numberWithBool:YES], @"seekable",
|
||||||
@"host", @"endian",
|
@"host", @"endian",
|
||||||
nil];
|
nil];
|
||||||
|
@ -145,7 +147,9 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in
|
||||||
|
|
||||||
- (int)readAudio:(void *)buf frames:(UInt32)frames
|
- (int)readAudio:(void *)buf frames:(UInt32)frames
|
||||||
{
|
{
|
||||||
if ( framesRead >= totalFrames )
|
BOOL repeat_one = IsRepeatOneSet();
|
||||||
|
|
||||||
|
if ( !repeat_one && framesRead >= totalFrames )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ( !ptmod )
|
if ( !ptmod )
|
||||||
|
@ -158,27 +162,27 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in
|
||||||
while ( total < frames ) {
|
while ( total < frames ) {
|
||||||
int framesToRender = 512;
|
int framesToRender = 512;
|
||||||
if ( framesToRender > totalFrames - framesRead )
|
if ( framesToRender > totalFrames - framesRead )
|
||||||
framesToRender = totalFrames - framesRead;
|
framesToRender = (int)(totalFrames - framesRead);
|
||||||
if ( framesToRender > frames - total )
|
if ( framesToRender > frames - total )
|
||||||
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 );
|
playptmod_Render( ptmod, sampleBuf, framesToRender );
|
||||||
|
|
||||||
if ( framesRead + framesToRender > framesLength ) {
|
if ( !repeat_one && framesRead + framesToRender > framesLength ) {
|
||||||
long fadeStart = ( framesLength > framesRead ) ? framesLength : framesRead;
|
long fadeStart = ( framesLength > framesRead ) ? framesLength : framesRead;
|
||||||
long fadeEnd = ( framesRead + framesToRender < totalFrames ) ? framesRead + framesToRender : totalFrames;
|
long fadeEnd = ( framesRead + framesToRender < totalFrames ) ? framesRead + framesToRender : totalFrames;
|
||||||
const long fadeTotal = totalFrames - framesLength;
|
const long fadeTotal = totalFrames - framesLength;
|
||||||
for ( long fadePos = fadeStart; fadePos < fadeEnd; ++fadePos ) {
|
for ( long fadePos = fadeStart; fadePos < fadeEnd; ++fadePos ) {
|
||||||
const long scale = ( fadeTotal - ( fadePos - framesLength ) );
|
const long scale = ( fadeTotal - ( fadePos - framesLength ) );
|
||||||
const long offset = fadePos - framesRead;
|
const long offset = fadePos - framesRead;
|
||||||
int16_t * samples = sampleBuf + offset * 2;
|
int32_t * samples = sampleBuf + offset * 2;
|
||||||
samples[ 0 ] = samples[ 0 ] * scale / fadeTotal;
|
samples[ 0 ] = (int32_t)(samples[ 0 ] * scale / fadeTotal);
|
||||||
samples[ 1 ] = samples[ 1 ] * scale / fadeTotal;
|
samples[ 1 ] = (int32_t)(samples[ 1 ] * scale / fadeTotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
framesToRender = fadeEnd - framesRead;
|
framesToRender = (int)(fadeEnd - framesRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !framesToRender )
|
if ( !framesToRender )
|
||||||
|
@ -188,6 +192,14 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in
|
||||||
framesRead += framesToRender;
|
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;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +212,14 @@ BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, in
|
||||||
return 0;
|
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;
|
framesRead = frame;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue