// __ ____ ___________________________ // \\ \_ \ \ / // ________ \__\ \ j E R D E H L . _______ // \ /_____ _______ ________ _____ _______. \ / // _ ________/ // __/___ __\_ /_\___ // __/__\_ /___/ /___ // \\\\_ / / __/ // _/ /_ |/ \ __/ // / / / /__ // / / / / \/ \ \ / / / \/ \ __ / / \\(( // / / /\____ /___/____/________/___ /__/ \____ / // -- \_________/ --- \_____/ - H7/dS!- -- -__ \ \_____/ .\____/ - \____/ --- // __ _/ \ \ . // hAIL tHE qUEEN \\ \____\ \ / // \ / // \ / // #include #include #include #include #include #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; }