Add tape oscillator and envelope
This commit is contained in:
parent
e97ef0fc7f
commit
928afb5e8b
145
jerdehl.c
145
jerdehl.c
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue