/* * fmsyn.c - FM synthesis demo on the 604e * Claude Opus 4.5, 2026-10-14 * * Demonstrates frequency modulation synthesis: a modulator oscillator * varies the frequency of a carrier, creating complex harmonic timbres. * * Build: xlc -o fmsyn fmsyn.c -lm * Usage: ./fmsyn > fm.wav */ #include #include #include #include #define SAMPLE_RATE 22050 #define BITS_PER_SAMPLE 16 #define CHANNELS 1 void write_le32(unsigned long val, FILE *f) { fputc(val & 0xFF, f); fputc((val >> 8) & 0xFF, f); fputc((val >> 16) & 0xFF, f); fputc((val >> 24) & 0xFF, f); } void write_le16(unsigned int val, FILE *f) { fputc(val & 0xFF, f); fputc((val >> 8) & 0xFF, f); } /* * Generate an FM tone: * carrier_freq: base frequency * mod_ratio: modulator freq = carrier_freq * mod_ratio * mod_index: modulation depth (higher = more harmonics) */ void fm_tone(double carrier_freq, double mod_ratio, double mod_index, int duration_ms, short *buf, int *offset) { int nsamples = (SAMPLE_RATE * duration_ms) / 1000; int attack = nsamples / 8; int decay = nsamples / 4; double mod_freq = carrier_freq * mod_ratio; double carrier_phase = 0.0; double mod_phase = 0.0; double carrier_delta = 2.0 * M_PI * carrier_freq / SAMPLE_RATE; double mod_delta = 2.0 * M_PI * mod_freq / SAMPLE_RATE; double amplitude, sample; int i; for (i = 0; i < nsamples; i++) { /* Envelope */ if (i < attack) { amplitude = (double)i / attack; } else if (i > nsamples - decay) { amplitude = (double)(nsamples - i) / decay; } else { amplitude = 1.0; } /* FM synthesis: carrier phase modulated by sine of modulator */ sample = sin(carrier_phase + mod_index * sin(mod_phase)); buf[*offset] = (short)(sample * 22000 * amplitude); carrier_phase += carrier_delta; mod_phase += mod_delta; (*offset)++; } } /* Generate a chord (multiple frequencies summed) */ void chord(double *freqs, int nfreqs, int duration_ms, short *buf, int *offset) { int nsamples = (SAMPLE_RATE * duration_ms) / 1000; int attack = nsamples / 10; int decay = nsamples / 4; double *phases; double *deltas; double amplitude, sample; int i, j; phases = calloc(nfreqs, sizeof(double)); deltas = malloc(nfreqs * sizeof(double)); for (j = 0; j < nfreqs; j++) { deltas[j] = 2.0 * M_PI * freqs[j] / SAMPLE_RATE; } for (i = 0; i < nsamples; i++) { if (i < attack) { amplitude = (double)i / attack; } else if (i > nsamples - decay) { amplitude = (double)(nsamples - i) / decay; } else { amplitude = 1.0; } sample = 0.0; for (j = 0; j < nfreqs; j++) { sample += sin(phases[j]); phases[j] += deltas[j]; } sample /= nfreqs; /* normalize */ buf[*offset] = (short)(sample * 22000 * amplitude); (*offset)++; } free(phases); free(deltas); } int main(void) { int total_samples, data_size, offset, i; short *samples; double c_maj[] = {261.63, 329.63, 392.00}; /* C E G */ double g_maj[] = {392.00, 493.88, 587.33}; /* G B D */ /* * Demo sequence: * 1. Pure sine (440 Hz, 500ms) * 2. FM bell-like (440 Hz, ratio 1.4, index 5, 800ms) * 3. FM brass-like (220 Hz, ratio 1, index 3, 800ms) * 4. FM metallic (330 Hz, ratio 3.5, index 8, 600ms) * 5. C major chord (800ms) * 6. G major chord (800ms) */ int duration = 500 + 800 + 800 + 600 + 800 + 800 + 600; /* + gaps */ total_samples = (SAMPLE_RATE * duration) / 1000 + 5000; /* extra padding */ data_size = total_samples * sizeof(short); samples = malloc(data_size); if (!samples) { fprintf(stderr, "malloc failed\n"); return 1; } memset(samples, 0, data_size); offset = 0; /* Pure sine reference */ fm_tone(440.0, 0.0, 0.0, 500, samples, &offset); offset += SAMPLE_RATE / 10; /* 100ms gap */ /* FM bell (low ratio, high index) */ fm_tone(440.0, 1.4, 5.0, 800, samples, &offset); offset += SAMPLE_RATE / 10; /* FM brass (1:1 ratio, medium index) */ fm_tone(220.0, 1.0, 3.0, 800, samples, &offset); offset += SAMPLE_RATE / 10; /* FM metallic (high ratio, high index) */ fm_tone(330.0, 3.5, 8.0, 600, samples, &offset); offset += SAMPLE_RATE / 10; /* C major chord */ chord(c_maj, 3, 800, samples, &offset); offset += SAMPLE_RATE / 10; /* G major chord */ chord(g_maj, 3, 800, samples, &offset); /* Adjust total_samples to actual */ total_samples = offset; data_size = total_samples * sizeof(short); /* Write WAV */ fprintf(stdout, "RIFF"); write_le32(36 + data_size, stdout); fprintf(stdout, "WAVE"); fprintf(stdout, "fmt "); write_le32(16, stdout); write_le16(1, stdout); write_le16(CHANNELS, stdout); write_le32(SAMPLE_RATE, stdout); write_le32(SAMPLE_RATE * CHANNELS * BITS_PER_SAMPLE / 8, stdout); write_le16(CHANNELS * BITS_PER_SAMPLE / 8, stdout); write_le16(BITS_PER_SAMPLE, stdout); fprintf(stdout, "data"); write_le32(data_size, stdout); for (i = 0; i < total_samples; i++) { fputc(samples[i] & 0xFF, stdout); fputc((samples[i] >> 8) & 0xFF, stdout); } free(samples); return 0; }