Untitled

 avatar
unknown
c_cpp
9 days ago
10 kB
10
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=NUM_OPS-1; j >= 0; 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_R: ampCalc = a*a*a;
						break;
					case ENV_AR_STAGE_A: ampCalc = 1.0f - 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]);
				/*
				oscCalc = DO_OSC(pOp->oscPhase) * ampCalc * pIns->opAmpMixOut[j];
				pOp->oscPhase += ((pV->baseDelta * pIns->opFreqMul[j]) + pIns->opDetune[j] + busPrevOp);
				*/
			}
			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    = {
		1.0,
		3.5, 
	},
	.opAmpMixOut     = {
		ATTEN(1.0),
		ATTEN(0),
	},
	.opAmpMixIn = {
		ATTEN(0),
		ATTEN(0.368),
	},
	.envIncr      = {
		{MS(20), MS(3000)},
		{MS(0), MS(1750)},
	},
};
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");
	printf("my inst %f\n", instr.envIncr[1][0]);
	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