stupidfm.c
unknown
c_cpp
9 days ago
9.9 kB
4
No Index
in 5 days
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <math.h>
#include <windows.h>
// https://github.com/mackron/miniaudio
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MA_NO_WAV
#define MA_NO_FLAC
#define MA_NO_MP3
#define MA_NO_RESOURCE_MANAGER
#define MA_NO_NODE_GRAPH
#define MA_NO_ENGINE
#define MA_NO_GENERATION
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#define AUDIOBUFSIZE 32
#define AUDIOBUFNUM 256
float audBuf[AUDIOBUFNUM * AUDIOBUFSIZE * 2] = {0};
uint32_t audIdxRead = ((AUDIOBUFNUM - 4) * AUDIOBUFSIZE);
uint32_t audIdxWrite = 0;
bool isPlayerActive = false;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) {
float* pBuf = (float*)pOutput;
uint32_t i;
for (uint32_t i=0; i < frameCount; i++) {
if (!isPlayerActive) return;
pBuf[i*2 + 0] = audBuf[audIdxRead*2 + 0];
pBuf[i*2 + 1] = audBuf[audIdxRead*2 + 1];
audIdxRead = (audIdxRead + 1) % (AUDIOBUFNUM * AUDIOBUFSIZE);
}
}
#define OPL3CLOCK 49716
#define BASE_A 440.0f
#define MIDI2DELTA(X) (BASE_A / (OPL3CLOCK * 32.0f)) * pow(2, (((float)(X) - 9) / 12.0f))
#define TWO_PIE 6.28318530717958647692528676655901f
#define DO_OSC(X) sin(TWO_PIE * (float)(X))
//#define DO_OSC(X) sinturns(X)
#define DO_ENV(X) ((X)*(X)*(X))
#define NUM_OPS 4
#define NUM_VOICES 8
float sinturns(float x) { // dirty sine
x = fmod(x, 1.0f);
if (fabs(x) < 0.50f) {
x -= 0.25f;
return (1.0f - 16.0f * x * x);
} else {
x -= 0.75f;
return (16.0f * x * x - 1.0f);
}
}
typedef struct {
char name[48];
float fbRatio;
float opDetune[NUM_OPS];
float opFreqMul[NUM_OPS];
float opAmpMixOut[NUM_OPS];
float opAmpMixIn[NUM_OPS];
float envIncr[NUM_OPS][2];
} StupidInstrument;
#define ENV_STAGE_IDLE 0
enum {
ENV_AR_STAGE_IDLE,
ENV_AR_STAGE_R,
ENV_AR_STAGE_A,
ENV_AR_NUM_STAGES,
};
typedef struct {
float oscPhase;
float envPos;
int envStage;
} StupidOperator;
typedef struct {
float baseDelta;
bool gate;
StupidOperator op[NUM_OPS];
} StupidVoice;
typedef struct {
StupidInstrument* pIns;
StupidVoice v[NUM_VOICES];
} StupidFm;
void SFM_init(StupidFm* pSynth) {
memset(pSynth, 0, sizeof(StupidFm));
}
void SFM_generateFrame(StupidFm* pSynth, float* pOut) {
StupidInstrument* pIns = pSynth->pIns;
pOut[1] = pOut[0] = 0.0f;
for (int i=0; i < NUM_VOICES; i++) {
StupidVoice* pV = &pSynth->v[i];
float busPrevOp = 0.0f;
float busOut = 0.0f;
for (int j=0; j < NUM_OPS; j++) {
StupidOperator* pOp = &pV->op[j];
float ampCalc = 0.0f;
float oscCalc = 0.0f;
if (pOp->envStage > ENV_STAGE_IDLE) {
/////////////
// envelope
float a = 1.0f - pOp->envPos;
switch (pOp->envStage) {
case ENV_AR_STAGE_A: ampCalc = 1.0f - a*a;
break;
case ENV_AR_STAGE_R: ampCalc = a*a*a;
break;
}
pOp->envPos += pIns->envIncr[j][ENV_AR_NUM_STAGES - (pOp->envStage + 1)];
if (pOp->envPos >= 1.0f) {
pOp->envPos = 0.0f;
pOp->envStage--;
}
/////////////
// oscilator
oscCalc = DO_OSC(pOp->oscPhase) * ampCalc;
pOp->oscPhase += ((pV->baseDelta * pIns->opFreqMul[j]) + pIns->opDetune[j] + (busPrevOp * pIns->opAmpMixIn[j]));
}
busPrevOp = oscCalc;
busOut += oscCalc * pIns->opAmpMixOut[j];
}
pOut[0] += busOut;
pOut[1] = pOut[0];
}
pOut[0] *= 0.1f;
pOut[1] *= 0.1f;
}
void SFM_gate(StupidFm* pSynth, uint8_t note, uint8_t voiceNum, bool isNoteOn) {
StupidVoice* pV = &pSynth->v[voiceNum];
assert(voiceNum < NUM_VOICES);
if (isNoteOn) {
pSynth->v[voiceNum].gate = true;
pSynth->v[voiceNum].baseDelta = MIDI2DELTA(note);
for (int i=0; i < NUM_OPS; i++) {
pSynth->v[voiceNum].op[i].oscPhase = 0.0f;
pSynth->v[voiceNum].op[i].envStage = ENV_AR_NUM_STAGES - 1;
pSynth->v[voiceNum].op[i].envPos = 0.0f;
}
} else {
pSynth->v[voiceNum].gate = false;
}
}
void SFM_setInstrument(StupidFm* pSynth, StupidInstrument* pInstr, uint8_t voiceNum) {
StupidVoice* pV = &pSynth->v[voiceNum];
assert(voiceNum < NUM_VOICES);
pSynth->v[voiceNum].gate = false;
}
/*
// TODO
typedef struct {
float detune;
float freqMul;
float ampLastOp; // How much last OP contributes to modulation. Last Amp controls feedback.
float ampInput; // How much input bus contributes to modulation
float ampOutput; // How much current OP outputs to output bus
float ampEnv;
float envIncr[2];
} FMInstrument_OP;
typedef struct {
char patchName[16];
float baseDelta;
int feedbackThrow; // How many operators does FeedBack include.
float ampOutputOutput; // How much outpus bus outputs(channel volume).
FMInstrument_OP op[NUM_OPS];
} FMInstrument;
// 1 1 1 10
// 1<(2 3<4)
*/
#define ALMOSTZERO 1e-30f
#define ATTEN(X) ((X)*(X)*(X))
#define MS(X) (1000.0f/(ALMOSTZERO + (float)(X)*(float)OPL3CLOCK))
#define DET(X) ((float)(X)/(ALMOSTZERO + (float)OPL3CLOCK))
StupidFm synth;
StupidInstrument instr = {
.fbRatio = 0.0,
.opDetune = {
0,
0,
},
.opFreqMul = {
3.5,
1.0,
},
.opAmpMixOut = {
ATTEN(0),
ATTEN(1.0),
},
.opAmpMixIn = {
ATTEN(0.0),
ATTEN(0.368),
},
.envIncr = {
{MS(0), MS(1750)},
{MS(20), MS(3000)},
},
};
StupidInstrument instrsine = {
.fbRatio = 0.0,
.opDetune = {
0,
},
.opFreqMul = {
1.0,
},
.opAmpMixOut = {
1.0,
},
.opAmpMixIn = {
0,
},
.envIncr = {
{MS(20), MS(3000)},
},
};
#define BM_GET(BMAP, IND) !!((BMAP)[(IND)>>3] & (1<<((IND)&7)))
#define BM_SET1(BMAP, IND) ((BMAP)[(IND)>>3] |= (1<<((IND)&7)))
#define BM_SET0(BMAP, IND) ((BMAP)[(IND)>>3] &= ~(1<<((IND)&7)))
char keyMap[] = "ZSXDCVGBHNJM" "Q2W3ER5T6Y7U" "I9O0P";
#define AUDIORATE 44100
int main(int argc, char *argv[]) {
ma_device_config config = {0};
ma_device device = {0};
config = ma_device_config_init(ma_device_type_playback);
config.sampleRate = AUDIORATE;
config.playback.format = ma_format_f32;
config.playback.channels = 2;
config.dataCallback = data_callback;
config.pUserData = NULL;
assert(MA_SUCCESS == ma_device_init(NULL, &config, &device));
SFM_init(&synth);
synth.pIns = &instr;
float tapsL[4] = {0};
float tapsR[4] = {0};
float delta = (float)OPL3CLOCK / (float)AUDIORATE;
float posFrac = 0;
uint8_t curVoice = 0;
int8_t octave = 4;
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
BYTE kbArr[256/8] = {0};
assert(hStdin != INVALID_HANDLE_VALUE);
//assert(sizeof(keyMap) == 12 + 12 + 5 +1);
isPlayerActive = true;
ma_device_start(&device);
printf("Hello FM.\n");
while (isPlayerActive) {
if (audIdxWrite/AUDIOBUFSIZE != audIdxRead/AUDIOBUFSIZE) {
while (audIdxWrite/AUDIOBUFSIZE != audIdxRead/AUDIOBUFSIZE) {
#define ITP_T04_SXX_F01_CUBIC(D, F) (D[1] + 0.5 * F*(D[2] - D[0] + F*(2.0 * D[0] - 5.0 * D[1] + 4.0 * D[2] - D[3] + F * (3.0 * (D[1] - D[2]) + D[3] - D[0]))))
#define INTERP_IN(BUF, X) BUF[0] = BUF[1];; BUF[1] = BUF[2];; BUF[2] = BUF[3];; BUF[3] = X
#define INTERP_OUT(BUF, F) ITP_T04_SXX_F01_CUBIC(BUF, F)
while (posFrac >= 1.0) {
float sampPair[2];
SFM_generateFrame(&synth, sampPair);
INTERP_IN(tapsL, (float)sampPair[0]);
INTERP_IN(tapsR, (float)sampPair[1]);
posFrac -= 1.0;
}
audBuf[audIdxWrite*2 + 0] = INTERP_OUT(tapsL, posFrac);
audBuf[audIdxWrite*2 + 1] = INTERP_OUT(tapsR, posFrac);
posFrac += delta;
audIdxWrite = (audIdxWrite + 1) % (AUDIOBUFNUM * AUDIOBUFSIZE);
}
} else {
SleepEx(10, 1);
}
// keypresses
DWORD unreadConEvents = 0;
#define CONBUFSIZE 32
while (GetNumberOfConsoleInputEvents(hStdin, &unreadConEvents) && unreadConEvents > 0) {
INPUT_RECORD conArr[CONBUFSIZE];
DWORD readConsoleEvents = 0;
if (ReadConsoleInput(hStdin, conArr, CONBUFSIZE, &readConsoleEvents)) {
for (int i=0; i < readConsoleEvents; i++) {
KEY_EVENT_RECORD* pKE = &conArr[i].Event.KeyEvent;
if (conArr[i].EventType != KEY_EVENT) continue;
if (pKE->wVirtualKeyCode > 0xFF) continue;
// filter keymatic repeat
if (BM_GET(kbArr, pKE->wVirtualKeyCode) == !!pKE->bKeyDown) continue;
pKE->bKeyDown ? BM_SET1(kbArr, pKE->wVirtualKeyCode) : BM_SET0(kbArr, pKE->wVirtualKeyCode);
if (!!pKE->bKeyDown)
BM_SET0(kbArr, pKE->wVirtualKeyCode);
else
BM_SET1(kbArr, pKE->wVirtualKeyCode);
switch (pKE->wVirtualKeyCode) {
case VK_ESCAPE:
isPlayerActive = false;
break;
case VK_UP:
if (!pKE->bKeyDown) {
if (octave+1 < 9) {
octave++;
printf("Octave: %d\n", octave);
}
}
break;
case VK_DOWN:
if (!pKE->bKeyDown) {
if (octave-1 >= 0) {
octave--;
printf("Octave: %d\n", octave);
}
}
break;
default:
for (int i=0; i < sizeof(keyMap)-1; i++) {
if (keyMap[i] == pKE->wVirtualKeyCode) {
if (pKE->bKeyDown) {
SFM_gate(&synth, octave*12 + i, curVoice, true);
curVoice = (curVoice+1) % NUM_VOICES;
} else {
//.....
}
break;
}
}
break;
}
}
} else {
break;
}
}
}
ma_device_uninit(&device);
}Editor is loading...
Leave a Comment