Skip to content

Instantly share code, notes, and snippets.

@yishengjiang99
Last active May 6, 2021 07:21
Show Gist options
  • Save yishengjiang99/d3d4b1d0e064e8157440ef9a8c620c9f to your computer and use it in GitHub Desktop.
Save yishengjiang99/d3d4b1d0e064e8157440ef9a8c620c9f to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "read2.h"
#include <math.h>
#include <unistd.h>
#include <time.h>
#define sr 48000
static float outputLeft[128];
static float outputRight[128];
static float outputInterweaved[128 * 2];
typedef struct _channel
{
voice *voice;
int program_number;
float midi_gain_cb;
float midi_pan;
} channel_t;
typedef struct
{
int sampleRate;
int currentFrame_start;
int samples_per_frame;
channel_t *channels;
} ctx_t;
int nvoices();
static ctx_t *ctx;
static voice *vptr;
int readsf(FILE *fd)
{
header_t *header = (header_t *)malloc(sizeof(header_t));
header2_t *h2 = (header2_t *)malloc(sizeof(header2_t));
fread(header, sizeof(header_t), 1, fd);
printf("%.4s %.4s %.4s %u\n", header->name, header->sfbk, header->list, header->size);
fread(h2, sizeof(header2_t), 1, fd);
printf("\n%.4s %u", h2->name, h2->size);
info = malloc(h2->size);
fread(info, h2->size, 1, fd);
fread(h2, sizeof(header2_t), 1, fd);
printf("\n%.4s %u", h2->name, h2->size);
data = (short *)malloc(h2->size * sizeof(short));
sdta = (float *)malloc(h2->size * sizeof(float));
float *trace = sdta;
nsamples = h2->size / sizeof(short);
printf("\n\t %ld", ftell(fd));
fread(data, sizeof(short), nsamples, fd);
for (int i = 0; i < nsamples; i++)
{
*trace++ = *(data + i) / 32767.0f;
}
//free(data);
#define readSection(section) \
fread(sh, sizeof(section_header), 1, fd); \
printf("%.4s:%u\n", sh->name, sh->size); \
n##section##s = sh->size / sizeof(section); \
section##s = (section *)malloc(sh->size); \
fread(section##s, sizeof(section), sh->size / sizeof(section), fd);
section_header *sh = (section_header *)malloc(sizeof(section_header));
fread(h2, sizeof(header2_t), 1, fd);
readSection(phdr);
readSection(pbag);
readSection(pmod);
readSection(pgen);
readSection(inst);
readSection(ibag);
readSection(imod);
readSection(igen);
readSection(shdr);
return 1;
}
static float p2over1200LUT[1200];
static inline float p2over1200(int x)
{
if (x < -12000)
return 0;
if (x < 0)
return 1.f / p2over1200(-x);
else if (x > 1200.0f)
{
return 2 * p2over1200(x - 1200.0f);
}
else
{
return p2over1200LUT[(unsigned short)(x)];
}
}
static float centdbLUT[960];
static float centdblut(int x)
{
if (x < 0)
x = 0;
if (x > 960)
x = 960;
return centdbLUT[x];
}
adsr_t *newEnvelope(short centAtt, short centRelease, short centDecay, short sustain, int sampleRate)
{
adsr_t *env = (adsr_t *)malloc(sizeof(adsr_t));
env->att_steps = fmax(p2over1200(centAtt) * sampleRate, 12);
env->decay_steps = fmax(p2over1200(centDecay) * sampleRate, 12);
env->release_steps = fmax(p2over1200(centRelease) * sampleRate, 12);
env->att_rate = -960.0f / env->att_steps;
env->decay_rate = -powf(10.0f, sustain / -200.0f) * powf(2.f, centDecay / 1200.0f);
env->release_rate = 960.0f / env->release_steps;
env->db_attenuate = 960.0f;
return env;
}
float envShift(adsr_t *env)
{
if (env->att_steps > 0)
{
env->att_steps--;
env->db_attenuate += env->att_rate;
}
else if (env->decay_steps > 0)
{
env->decay_steps--;
float egval = (1 - env->db_attenuate) * 960.0f;
egval *= env->decay_rate;
env->db_attenuate = 960.0f * egval - egval;
}
else if (env->release_steps > 0)
{
env->release_steps--;
env->db_attenuate += env->release_rate;
}
if (env->db_attenuate > 960)
{
env->db_attenuate = 960.0f;
}
if (env->db_attenuate < 0.0)
{
env->db_attenuate = 0.0f;
}
return env->db_attenuate;
}
void adsrRelease(adsr_t *env)
{
env->decay_steps = 0;
env->att_steps = 0;
}
voice *newVoice(zone_t *z, int key, int vel)
{
voice *v = (voice *)malloc(sizeof(voice));
v->ampvol = newEnvelope(z->VolEnvAttack, z->VolEnvRelease, z->VolEnvDecay, z->VolEnvSustain, 48000);
short rt = z->OverrideRootKey > -1 ? z->sample->originalPitch : z->sample->originalPitch;
float sampleTone = rt * 100.0f + z->CoarseTune * 100.0f + (float)z->FineTune;
float octaveDivv = ((float)key * 100 - sampleTone) / 1200.0f;
v->ratio = 1.0f * pow(2.0f, octaveDivv) * z->sample->sampleRate / 48000;
v->pos = z->start;
v->frac = 0.0f;
v->z = z;
v->midi = key;
v->velocity = vel;
v->next = NULL;
return v;
}
void index22(int pid, int bkid, int key, int vel, int chid)
{
short igset[60] = {0};
int instID = -1;
int lastSampId = -1;
short pgdef[60] = {0};
for (int i = 0; i < nphdrs - 1; i++)
{
if (phdrs[i].bankId != bkid || phdrs[i].pid != pid)
continue;
int lastbag = phdrs[i + 1].pbagNdx;
for (int j = phdrs[i].pbagNdx; j < lastbag; j++)
{
pbag *pg = pbags + j;
pgen_t *lastg = pgens + pg[j + 1].pgen_id;
int pgenId = pg->pgen_id;
int lastPgenId = j < npbags - 1 ? pbags[j + 1].pgen_id : npgens - 1;
short pgset[60] = {0};
instID = -1;
for (int k = pgenId; k < lastPgenId; k++)
{
pgen_t *g = pgens + k;
if (g->operator== 44 &&(g->val.ranges.lo > vel || g->val.ranges.hi < vel))
break;
if (g->operator== 43 &&(g->val.ranges.lo > key || g->val.ranges.hi < key))
break;
if (g->operator== 41)
{
instID = g->val.shAmount;
}
pgset[g->operator] = g->val.shAmount;
}
if (instID == -1)
{
memcpy(pgdef, pgset, sizeof(pgset));
}
else
{
inst *ihead = insts + instID;
int ibgId = ihead->ibagNdx;
int lastibg = (ihead + 1)->ibagNdx;
lastSampId = -1;
short igdef[60] = {0};
for (int ibg = ibgId; ibg < lastibg; ibg++)
{
short igset[60];
ibag *ibgg = ibags + ibg;
pgen_t *lastig = ibg < nibags - 1 ? igens + (ibgg + 1)->igen_id : igens + nigens - 1;
for (pgen_t *g = igens + ibgg->igen_id; g->operator!= 60 && g != lastig; g++)
{
if (g->operator== 44 &&(g->val.ranges.lo > vel || g->val.ranges.hi < vel))
break;
if (g->operator== 43 &&(g->val.ranges.lo > key || g->val.ranges.hi < key))
break;
igset[g->operator]=g->val.shAmount;
if (g->operator== 53)
{
lastSampId = g->val.shAmount; // | (ig->val.ranges.hi << 8);
short *attributes = (short *)malloc(sizeof(short) * 60);
for (int i = 0; i < 60; i++)
{
if (igset[i])
*(attributes + i) = igset[i];
else if (igdef[i])
*(attributes + i) = igdef[i];
if (pgset[i])
*(attributes + i) += pgset[i];
else if (pgdef[i])
*(attributes + i) += pgdef[i];
}
zone_t *z = (zone_t *)malloc(sizeof(zone_t));
memcpy(z, attributes, 60 * sizeof(short));
shdrcast *sh = (shdrcast *)(shdrs + lastSampId);
z->sample = sh;
z->start = sh->start + ((unsigned short)z->StartAddrCoarseOfs << 15) + (unsigned short)z->StartAddrOfs;
z->end = sh->end + ((unsigned short)(z->EndAddrCoarseOfs << 15)) + (unsigned short)z->EndAddrOfs;
z->endloop = sh->endloop + ((unsigned short)z->EndLoopAddrCoarseOfs << 15) + (unsigned short)z->EndLoopAddrOfs;
z->startloop = sh->startloop + (unsigned short)(z->StartLoopAddrCoarseOfs << 15) + (unsigned short)z->StartLoopAddrOfs;
voice *v = vptr;
voice *nv = newVoice(z, key, vel);
nv->chid = chid;
int added = 0;
while (v->next != NULL)
{
if (v->next->chid == chid && v->next->ampvol->db_attenuate > 920.0f)
{
nv->next = v->next->next;
v->next = nv;
break;
}
v = v->next;
}
if (!added)
v->next = nv;
}
}
}
}
}
}
}
void initLUTs()
{
for (int i = 0; i < 1200; i++)
{
p2over1200LUT[i] = powf(2.0f, i / 1200.0f);
}
for (int i = 0; i < 960; i++)
{
centdbLUT[i] = powf(10.0f, i / 200);
}
vptr = (voice *)malloc(sizeof(voice));
}
void loop(voice *v)
{
uint32_t loopLength = v->z->endloop - v->z->startloop;
int cb_attentuate = v->z->Attenuation;
int16_t pan = v->z->Pan;
float panLeft = 0.5f - (float)pan / 1000.0f;
float panright = 0.5f + (float)pan / 1000.0f;
for (int i = 0; i < 128; i++)
{
float f1 = *(sdta + v->pos);
float f2 = *(sdta + v->pos + 1);
float gain = f1 + (f2 - f1) * v->frac;
float mono = gain * centdblut(envShift(v->ampvol) - cb_attentuate);
outputInterweaved[2 * i] += mono * panLeft;
outputInterweaved[2 * i + 1] += mono * panright;
v->frac += v->ratio;
while (v->frac >= 1.0f)
{
v->frac--;
v->pos++;
}
if (v->pos >= v->z->sample->endloop)
{
v->pos -= loopLength;
}
}
}
void render(int frames, FILE *output)
{
while (frames >= 0)
{
voice *t = vptr;
bzero(outputInterweaved, sizeof(float) * ctx->samples_per_frame);
while (t->next != NULL)
{
t = t->next;
loop(t);
// assert(v->ampvol->db_attenuate != 0);
}
frames -= 128;
fwrite(outputInterweaved, 128, 4, output);
}
}
void noteOn(int channelNumber, int midi, int vel)
{
int programNumber = (ctx->channels + channelNumber)->program_number;
printf("\n notes n %d", nvoices());
index22(programNumber & 0x7f, programNumber & 0x80, midi, vel, channelNumber);
printf("\n notes n %d", nvoices());
}
void noteOff(int channelNumber, int midi)
{
voice *v = vptr;
while (v->next)
{
if (v->next->chid == channelNumber && v->next->midi == midi)
{
adsrRelease(v->next->ampvol);
break;
}
v = v->next;
}
}
void init_ctx()
{
ctx = (ctx_t *)malloc(sizeof(ctx_t));
ctx->sampleRate = 48000;
ctx->currentFrame_start = 0;
ctx->samples_per_frame = 127;
ctx->channels = (channel_t *)malloc(sizeof(channel_t) * 16);
for (int i = 0; i < 16; i++)
{
(ctx->channels + i)->program_number = 0;
(ctx->channels + i)->midi_pan = 1.f;
(ctx->channels + i)->midi_gain_cb = 89.0f;
(ctx->channels + i)->voice = (voice *)malloc(sizeof(voice));
}
vptr = (voice *)malloc(sizeof(voice));
vptr->next = NULL;
}
int mkfifo(char *sf);
int nvoices()
{
int n = 0;
voice *t = vptr;
while (t != NULL && t->next != NULL)
{
t = t->next;
n++;
}
return n;
}
#include "biquad.c"
int main()
{
// FILE *dl = popen("curl -s 'https://www.grepawk.com/static/Timpani-20201121.sf2' -o -", "r");
// printf("\n%f, ", stbl[i]);
init_ctx();
printf("%p", vptr);
initLUTs();
FILE *dl = fopen("Acoustic%20Guitar.sf2", "rb");
if (!dl)
{
dl = popen("curl -s 'https://grep32bit.blob.core.windows.net/sf2/Acoustic%20Guitar.sf2' -O", "r");
}
readsf(dl);
fclose(dl);
for (int i = 0; i < nphdrs; i++)
printf("\n %d %s %d", i, phdrs[i].name, phdrs[i].pid);
init_ctx();
ctx->channels[0].program_number = phdrs[0].pid;
ctx->channels[1].program_number = phdrs[0].pid;
FILE *proc = popen("ffplay -loglevel panic -ar 48000 -ac 2 -nodisp -f f32le -i pipe:0", "w");
biquad *b = BiQuad_new(LPF, 1, 10000, 48000, 1);
printf("%f", BiQuad(0, b));
noteOn(0, 44, 128);
render(128);
return 0;
}
char *generator[60] = {"Gen_StartAddrOfs", "Gen_EndAddrOfs", "Gen_StartLoopAddrOfs", "Gen_EndLoopAddrOfs", "Gen_StartAddrCoarseOfs", "Gen_ModLFO2Pitch", "Gen_VibLFO2Pitch", "Gen_ModEnv2Pitch", "Gen_FilterFc", "Gen_FilterQ", "Gen_ModLFO2FilterFc", "Gen_ModEnv2FilterFc", "Gen_EndAddrCoarseOfs", "Gen_ModLFO2Vol", "Gen_Unused1", "Gen_ChorusSend", "Gen_ReverbSend", "Gen_Pan", "Gen_Unused2", "Gen_Unused3", "Gen_Unused4", "Gen_ModLFODelay", "Gen_ModLFOFreq", "Gen_VibLFODelay", "Gen_VibLFOFreq", "Gen_ModEnvDelay", "Gen_ModEnvAttack", "Gen_ModEnvHold", "Gen_ModEnvDecay", "Gen_ModEnvSustain", "Gen_ModEnvRelease", "Gen_Key2ModEnvHold", "Gen_Key2ModEnvDecay", "Gen_VolEnvDelay", "Gen_VolEnvAttack", "Gen_VolEnvHold", "Gen_VolEnvDecay", "Gen_VolEnvSustain", "Gen_VolEnvRelease", "Gen_Key2VolEnvHold", "Gen_Key2VolEnvDecay", "Gen_Instrument", "Gen_Reserved1", "Gen_KeyRange", "Gen_VelRange", "Gen_StartLoopAddrCoarseOfs", "Gen_Keynum", "Gen_Velocity", "Gen_Attenuation", "Gen_Reserved2", "Gen_EndLoopAddrCoarseOfs", "Gen_CoarseTune", "Gen_FineTune", "Gen_SampleId", "Gen_SampleModes", "Gen_Reserved3", "Gen_ScaleTune", "Gen_ExclusiveClass", "Gen_OverrideRootKey", "Gen_Dummy"};
typedef uint32_t DWORD; // uint32_t;
typedef DWORD FOURCC;
typedef struct
{
uint8_t lo, hi;
} rangesType; // Four-character code
typedef struct
{
FOURCC ckID; // A chunk ID identifies the type of data within the chunk.
DWORD ckSize; // The size of the chunk data in bytes, excluding any pad byte.
char *ckDATA; // The actual data plus a pad byte if req'd to word align.
} RIFFCHUNKS;
typedef union
{
rangesType ranges;
short shAmount;
unsigned short uAmount;
} genAmountType;
typedef struct
{
char name[4];
unsigned int size;
char sfbk[4];
char list[4];
} header_t;
typedef struct
{
unsigned int size;
char name[4];
} header2_t;
typedef struct
{
char name[4];
unsigned int size;
} section_header;
typedef enum
{
monoSample = 1,
rightSample = 2,
leftSample = 4,
linkedSample = 8,
RomMonoSample = 0x8001,
RomRightSample = 0x8002,
RomLeftSample = 0x8004,
RomLinkedSample = 0x8008
} SFSampleLink;
typedef struct
{
char name[4];
unsigned int size;
char *data;
} pdta;
typedef struct
{
char name[20];
uint16_t pid, bankId, pbagNdx;
char idc[12];
} phdr;
typedef struct
{
unsigned short pgen_id, pmod_id;
} pbag;
typedef struct
{
unsigned short igen_id, imod_id;
} ibag;
typedef struct
{
unsigned short operator;
genAmountType val;
} pgen_t;
typedef pgen_t pgen;
typedef struct
{
char data[10];
} pmod;
typedef struct
{
char name[20];
unsigned short ibagNdx;
} inst;
typedef struct
{
char data[10];
} imod;
typedef union
{
uint8_t hi, lo;
unsigned short val;
short word;
} gen_val;
typedef pgen_t igen;
typedef struct
{
// shdr's 46 byters is malloc aligned to 48 and I don't make it stop
// so we first read 46 chars explicitly and then casted to shdrcast defined
//beloe
uint8_t dc[46];
} shdr;
typedef struct
{
char name[20];
uint32_t start, end, startloop, endloop, sampleRate;
int16_t originalPitch;
int8_t pitchCorrection, sampleMode;
} shdrcast;
typedef struct
{
short StartAddrOfs, EndAddrOfs, StartLoopAddrOfs, EndLoopAddrOfs, StartAddrCoarseOfs, ModLFO2Pitch, VibLFO2Pitch, ModEnv2Pitch, FilterFc, FilterQ, ModLFO2FilterFc, ModEnv2FilterFc, EndAddrCoarseOfs, ModLFO2Vol, Unused1, ChorusSend, ReverbSend, Pan, Unused2, Unused3, Unused4, ModLFODelay, ModLFOFreq, VibLFODelay, VibLFOFreq, ModEnvDelay, ModEnvAttack, ModEnvHold, ModEnvDecay, ModEnvSustain, ModEnvRelease, Key2ModEnvHold, Key2ModEnvDecay, VolEnvDelay, VolEnvAttack, VolEnvHold, VolEnvDecay, VolEnvSustain, VolEnvRelease, Key2VolEnvHold, Key2VolEnvDecay, Instrument, Reserved1, KeyRange, VelRange, StartLoopAddrCoarseOfs, Keynum, Velocity, Attenuation, Reserved2, EndLoopAddrCoarseOfs, CoarseTune, FineTune, SampleId, SampleModes, Reserved3, ScaleTune, ExclusiveClass, OverrideRootKey, Dummy;
shdrcast *sample;
} zone_t;
typedef struct
{
uint32_t att_steps, decay_steps, release_steps;
unsigned short sustain;
float db_attenuate;
float att_rate, decay_rate, release_rate;
} adsr_t;
typedef struct
{
zone_t *z;
uint32_t pos;
float frac;
float ratio;
adsr_t *ampvol;
int midi;
int velocity;
} voice;
static int nphdrs, npbags, npgens, npmods, nshdrs, ninsts, nimods, nigens, nibags, nshrs;
static phdr *phdrs;
static pbag *pbags;
static pmod *pmods;
static pgen *pgens;
static inst *insts;
static ibag *ibags;
static imod *imods;
static igen *igens;
static shdr *shdrs;
static short *data;
static void *info;
static int nsamples;
static float *sdta;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment