Skip to content

Instantly share code, notes, and snippets.

@jaames
Last active April 2, 2022 23:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaames/ee3c1e4116e752b61cc8d075a198b9c0 to your computer and use it in GitHub Desktop.
Save jaames/ee3c1e4116e752b61cc8d075a198b9c0 to your computer and use it in GitHub Desktop.
Example for decoding and playing an ADPCM audio buffer on the Playdate in C, only supports mono IMA-ADPCM with no blocks!
// adpcm.h
#define CLAMP(n, l, h) \
if (n < l) n = l; \
if (n > h) n = h;
#define min(a,b) (((a) < (b)) ? (a) : (b))
static const int8_t indexTable[8] =
{ -1, -1, -1, -1, 2, 4, 6, 8 };
static const int16_t stepTable[89] =
{
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
static int diffTable[89][16];
static int32_t predictor;
static int8_t stepIndex;
typedef struct adpcmplay_ctx
{
int16_t* pcmBuff;
AudioSample* sample;
SamplePlayer* player;
} adpcmplay_ctx;
// adpcm.c
// Call once to set up diff table
void adpcmInit()
{
int16_t step;
int diff;
for (u16 stepIndex = 0; stepIndex < 89; stepIndex++)
{
step = stepTable[stepIndex];
for (u16 sample = 0; sample < 16; sample++)
{
diff = step >> 3;
if (sample & 1) diff += step >> 2;
if (sample & 2) diff += step >> 1;
if (sample & 4) diff += step;
if (sample & 8) diff = -diff;
diffTable[stepIndex][sample] = diff;
}
}
}
// Converts a single 4-bit adpcm sample to pcm
int16_t adpcmDecodeSample(uint8_t sample)
{
predictor += diffTable[stepIndex][sample];
CLAMP(predictor, -32768, 32767);
stepIndex += indexTable[sample & 7];
CLAMP(stepIndex, 0, 88);
return (int16_t)predictor;
}
// Converts adpcm buffer (in) to pcm buffer (out)
// assumes mono-channel audio buffer
void adpcmDecodeBuffer(const uint8_t* in, int16_t* out, int length)
{
predictor = 0;
stepIndex = 0;
while (length--)
{
*out++ = adpcmDecodeSample(*in & 0xF);
*out++ = adpcmDecodeSample(*in++ >> 4);
}
}
// Creates a sample player for an adpcm buffer
adpcmplay_ctx* adpcmPlayerNew(const uint8_t* in, int numSamples, int sampleRate)
{
adpcmplay_ctx* ctx = pd->system->realloc(NULL, sizeof(adpcmplay_ctx));
int adpcmSize = numSamples * sizeof(uint8_t) / 2;
int pcmSize = numSamples * sizeof(int16_t);
// Allocate a pcm buffer
ctx->pcmBuff = pd->system->realloc(NULL, pcmSize);
// Decode ADPCM
adpcmDecodeBuffer(in, ctx->pcmBuff, adpcmSize);
// Create an audio sample from the data buffer
// the data buffer isn't copied, so it *should* be possible to update it on the fly and hear that reflected in the audio
ctx->sample = pd->sound->sample->newSampleFromData((uint8_t*)ctx->pcmBuff, kSound16bitMono, sampleRate, pcmSize);
ctx->player = pd->sound->sampleplayer->newPlayer();
// Create a sample player with the given sample
pd->sound->sampleplayer->setSample(ctx->player, ctx->sample);
return ctx;
}
void adpcmPlayerPlay(ctx)
{
pd->sound->sampleplayer->play(ctx->player, 1, 1.0);
}
void adpcmPlayerStop(ctx)
{
pd->sound->sampleplayer->stop(ctx->player);
}
// Cleans up the sample player and the pcm buffer
void adpcmPlayerFree(ctx)
{
pd->sound->sampleplayer->stop(ctx->player);
pd->sound->sampleplayer->freePlayer(ctx->player);
pd->sound->sample->freeSample(ctx->sample);
pd->system->realloc(ctx->pcmBuff, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment