1
0
Fork 0

Add tape oscillator and envelope

This commit is contained in:
Rodrigo Franco 2024-01-17 07:23:13 -03:00
parent e97ef0fc7f
commit 928afb5e8b
1 changed files with 113 additions and 32 deletions

145
jerdehl.c
View File

@ -23,6 +23,12 @@
#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];
@ -45,34 +51,18 @@ typedef struct noise_oscillator
double value;
} noise_osc_t;
double generate_noise(noise_osc_t *n)
typedef struct lfo
{
// A simple pseudo-random noise generator
n->seed = (n->seed * 1235515245 + 12345) & 0x7ffaffff;
return (double)n->seed / 0x40000000 - 1.0; // Normalize to range -1.0 to 1.0
}
double phase;
double freq;
} lfo_t;
void init_noise_osc(noise_osc_t *n, uint32_t seed)
typedef struct tape_oscillator
{
n->seed = seed;
}
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) < 20) // 10% chance for a louder crackle
{
volume += 15.0; // Increase volume for a louder crackle
}
// Apply the volume to the noise
noise *= volume;
// Clamp the noise to avoid clipping
return noise;
}
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)
@ -93,12 +83,37 @@ float higherChordFrequencies[CHORD_TYPES][CHORD_NOTES] = {
{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;
@ -107,12 +122,57 @@ void update_osc(osc_t *o)
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 (2/3 of the duration)
int fadeOutStart = (p->noteDuration * 1) / 3;
// 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;
@ -146,11 +206,13 @@ void update_piano_osc(PianoOscillator *p, float chordFrequencies[CHORD_TYPES][CH
}
}
}
int main()
{
FILE *fp;
PianoOscillator pianoOsc;
PianoOscillator highPianoOsc;
tape_osc_t tapeOsc;
osc_t env;
uint16_t out16;
float out = 0.0f;
@ -162,18 +224,22 @@ int main()
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 first chord and duration for piano and high piano
// 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
// Set the frequencies for the first chord
int chordIndex = rand() % CHORD_TYPES;
for (int i = 0; i < CHORD_NOTES; ++i)
{
@ -181,10 +247,18 @@ int main()
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
@ -194,13 +268,19 @@ int main()
// Update the high piano oscillator
update_piano_osc(&highPianoOsc, higherChordFrequencies, 2000); // 2 for a 1 in 2 chance of silence
// Mix the outputs from all oscillators
// 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)
{
@ -209,7 +289,7 @@ int main()
}
// Increase the gain to make the signal louder
float gain = 0.065f; // Adjust the gain factor as needed
float gain = 0.4f;
out *= gain;
// Ensure the signal does not exceed the maximum range
@ -225,6 +305,7 @@ int main()
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;
@ -236,4 +317,4 @@ int main()
}
fclose(fp);
return 0;
}
}