1
0
Fork 0
jerdehl/jerdehl.c

320 lines
9.5 KiB
C

// __ ____ ___________________________
// \\ \_ \ \ /
// ________ \__\ \ j E R D E H L . _______
// \ /_____ _______ ________ _____ _______. \ /
// _ ________/ // __/___ __\_ /_\___ // __/__\_ /___/ /___
// \\\\_ / / __/ // _/ /_ |/ \ __/ // / / / /__
// / / / / \/ \ \ / / / \/ \ __ / / \\((
// / / /\____ /___/____/________/___ /__/ \____ /
// -- \_________/ --- \_____/ - H7/dS!- -- -__ \ \_____/ .\____/ - \____/ ---
// __ _/ \ \ .
// hAIL tHE qUEEN \\ \____\ \ /
// \ /
// \ /
//
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define SINE_TABLE_SIZE 2048
#define UINT32_MAX_HALF (UINT32_MAX / 2)
#define CHORD_NOTES 3
#define CHORD_TYPES 7
#define SEED_MULTIPLIER 1235515245
#define SEED_INCREMENT 12345
#define SEED_MASK 0x7ffaffff
#define SEED_NORMALIZER 0x40000000
#define VOLUME_SPIKE_CHANCE 20
#define VOLUME_SPIKE_INCREASE 15.0
double sinetab[SINE_TABLE_SIZE];
typedef struct oscillator
{
uint32_t phasor;
uint32_t step;
double value;
} osc_t;
typedef struct
{
osc_t noteOsc[CHORD_NOTES];
int noteDuration;
} PianoOscillator;
typedef struct noise_oscillator
{
uint32_t seed;
double value;
} noise_osc_t;
typedef struct lfo
{
double phase;
double freq;
} lfo_t;
typedef struct tape_oscillator
{
lfo_t wowLFO;
lfo_t flutterLFO;
double saturation;
} tape_osc_t;
float chordFrequencies[CHORD_TYPES][CHORD_NOTES] = {
{65.41, 77.78, 98.00}, // Cm (C, Eb, G)
{73.42, 87.31, 97.99}, // Ddim (D, F, Ab)
{82.41, 97.99, 123.47}, // Eb (Eb, G, Bb)
{87.31, 103.83, 130.81}, // Fm (F, Ab, C)
{97.99, 116.54, 146.83}, // Gm (G, Bb, D)
{103.83, 123.47, 155.56}, // Ab (Ab, C, Eb)
{116.54, 130.81, 155.56}, // Bb (Bb, D, F)
};
float higherChordFrequencies[CHORD_TYPES][CHORD_NOTES] = {
{130.81, 155.56, 196.00}, // Cm (C4, Eb4, G4)
{146.83, 174.61, 195.99}, // Ddim (D4, F4, Ab4)
{164.81, 195.99, 246.94}, // Eb (Eb4, G4, Bb4)
{174.61, 207.65, 261.63}, // Fm (F4, Ab4, C5)
{195.99, 233.08, 293.66}, // Gm (G4, Bb4, D5)
{207.65, 246.94, 311.13}, // Ab (Ab4, C5, Eb5)
{233.08, 261.63, 311.13}, // Bb (Bb4, D5, F5)
};
void init_lfo(lfo_t *lfo, double freq)
{
lfo->phase = 0.0;
lfo->freq = freq;
}
void init_tape_osc(tape_osc_t *t, double saturation)
{
init_lfo(&t->wowLFO, 0.1); // Adjust the frequency to control the speed of wow
init_lfo(&t->flutterLFO, 5.0); // Adjust the frequency to control the speed of flutter
t->saturation = saturation;
}
void init_noise_osc(noise_osc_t *n, uint32_t seed)
{
n->seed = seed;
}
void set_osc_freq(osc_t *o, float f)
{
o->step = (uint32_t)((f / 16000.0) * UINT32_MAX);
o->phasor = 0; // Reset the phasor to start at the beginning of the sine wave
}
double generate_noise(noise_osc_t *n)
{
// A simple pseudo-random noise generator
n->seed = (n->seed * SEED_MULTIPLIER + SEED_INCREMENT) & SEED_MASK;
return (double)n->seed / SEED_NORMALIZER - 1.0; // Normalize to range -1.0 to 1.0
}
void update_osc(osc_t *o)
{
uint16_t index;
o->phasor += o->step;
index = o->phasor >> (32 - 11); // We are using an 11-bit index for a 2048-entry table
o->value = sinetab[index] > 0 ? 1.0 : -1.0;
}
double update_lfo(lfo_t *lfo, double dt)
{
lfo->phase += lfo->freq * dt;
if (lfo->phase > 1.0)
lfo->phase -= 1.0;
return sin(lfo->phase * 2 * M_PI);
}
double update_crackle(noise_osc_t *n)
{
// Generate the base noise
double noise = generate_noise(n);
// Apply random volume modulation to simulate the crackling effect
double volume = 0 + ((double)rand() / RAND_MAX) * 0.2; // Random volume between 0.2 and 1.0
// Occasionally spike the volume to simulate louder crackles
if ((rand() % 100) < VOLUME_SPIKE_CHANCE) // 20% chance for a louder crackle
{
volume += VOLUME_SPIKE_INCREASE; // Increase volume for a louder crackle
}
// Apply the volume to the noise
noise *= volume;
// Clamp the noise to avoid clipping
return noise;
}
double update_tape_osc(tape_osc_t *t, double input, double dt)
{
// Apply wow and flutter
double wow = 0.01 * update_lfo(&t->wowLFO, dt); // Adjust the multiplier to control the amount of wow
double flutter = 0.005 * update_lfo(&t->flutterLFO, dt); // Adjust the multiplier to control the amount of flutter
input += wow + flutter;
// Apply simple non-linear distortion to simulate tape saturation
if (input > t->saturation)
{
input = t->saturation - exp(-input);
}
else if (input < -t->saturation)
{
input = -t->saturation + exp(input);
}
return input;
}
void update_piano_osc(PianoOscillator *p, float chordFrequencies[CHORD_TYPES][CHORD_NOTES], int notePlayChance)
{
if (p->noteDuration > 0)
{
// Calculate the point at which the note should start fading out (1/2 of the duration)
int fadeOutStart = (p->noteDuration * 1) / 2;
// Calculate the volume scaling factor based on the remaining duration
double volumeScale = 1.0;
if (p->noteDuration < fadeOutStart)
{
// Linear fade out to zero by the end of the note duration
volumeScale = (double)p->noteDuration / fadeOutStart;
}
// Play the current chord with volume scaling
for (int i = 0; i < CHORD_NOTES; ++i)
{
update_osc(&p->noteOsc[i]);
p->noteOsc[i].value *= volumeScale; // Apply the volume scaling factor
}
p->noteDuration--; // Decrement the note duration
}
else
{
// Randomly decide whether to start a new chord
if (rand() % notePlayChance == 0)
{
int chordIndex = rand() % CHORD_TYPES;
for (int i = 0; i < CHORD_NOTES; ++i)
{
set_osc_freq(&p->noteOsc[i], chordFrequencies[chordIndex][i]);
}
// Set a new random note duration between 5 and 10 seconds
p->noteDuration = (rand() % (2 * 16000)) + (5 * 16000);
}
}
}
int main()
{
FILE *fp;
PianoOscillator pianoOsc;
PianoOscillator highPianoOsc;
tape_osc_t tapeOsc;
osc_t env;
uint16_t out16;
float out = 0.0f;
fp = fopen("/dev/stdout", "wb");
if (!fp)
{
perror("Error opening output file");
return 1;
}
// Set random seed
srand(time(NULL));
// Initialize the sine table
for (int i = 0; i < SINE_TABLE_SIZE; i++)
{
sinetab[i] = sin(i * 2 * M_PI / SINE_TABLE_SIZE);
}
// Initialize the piano oscillator
init_tape_osc(&tapeOsc, 0.7); // Adjust the saturation level
// Initialize duration and the first chord for both pianos
pianoOsc.noteDuration = (rand() % (2 * 16000)) + (5 * 16000); // Random duration between 5 and 10 seconds
highPianoOsc.noteDuration = (rand() % (2 * 16000)) + (5 * 16000); // Random duration between 5 and 10 seconds
int chordIndex = rand() % CHORD_TYPES;
for (int i = 0; i < CHORD_NOTES; ++i)
{
set_osc_freq(&pianoOsc.noteOsc[i], chordFrequencies[chordIndex][i]);
set_osc_freq(&highPianoOsc.noteOsc[i], higherChordFrequencies[chordIndex][i]);
}
// Initialize the noise oscillator for fire crackling
noise_osc_t fireCrackleOsc;
init_noise_osc(&fireCrackleOsc, 1702970159);
// Initialize the envelope oscillator
set_osc_freq(&env, 0.002);
env.phasor = 0.075*UINT32_MAX;
// Initialize the output buffer
int j = 20000000;
// Main loop
while (1)
{
out = 0.0f; // Reset output each iteration
// Update the piano oscillator
update_piano_osc(&pianoOsc, chordFrequencies, 1000); // 10 for a 1 in 10 chance of silence
// Update the high piano oscillator
update_piano_osc(&highPianoOsc, higherChordFrequencies, 2000); // 2 for a 1 in 2 chance of silence
// Mix the pianos
for (int i = 0; i < CHORD_NOTES; ++i)
{
out += pianoOsc.noteOsc[i].value + (highPianoOsc.noteOsc[i].value * -1.0f);
}
out /= (2 * CHORD_NOTES); // Correctly average the mixed notes, considering both piano and high piano
// Apply tape wow and flutter
out = update_tape_osc(&tapeOsc, out, 1.0 / 16000.0);
// Envelope modulation
out *=(0.5 + 0.2*env.value);
// Fire crackling noise
if (j % 2000 == 0)
{
double crackle = update_crackle(&fireCrackleOsc);
out += crackle * 2.0f;
}
// Increase the gain to make the signal louder
float gain = 0.4f;
out *= gain;
// Ensure the signal does not exceed the maximum range
if (out > 1.0f)
out = 1.0f;
if (out < -1.0f)
out = -1.0f;
// Scale to 16-bit output value
out16 = (uint16_t)((UINT16_MAX / 2) * (0.6 + 1.0 * out));
// Write output value for stereo channels
fwrite(&out16, sizeof(out16), 1, fp);
fwrite(&out16, sizeof(out16), 1, fp);
// If we have reached the end of the output buffer, reset the counter
if (j == 0)
{
j = 20000000;
}
else
{
j--;
}
}
fclose(fp);
return 0;
}