Skip to content

Instantly share code, notes, and snippets.

@skejeton
Last active February 20, 2023 12:45
Show Gist options
  • Save skejeton/af02832b9a2566ec796b48dc0461be68 to your computer and use it in GitHub Desktop.
Save skejeton/af02832b9a2566ec796b48dc0461be68 to your computer and use it in GitHub Desktop.
Generates Mario Theme Song
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define NUM_SAMPLES 44100
#define PI 3.14159265
// ------ NOTE IDS ------
#define C_ 0
#define CS 1
#define D_ 2
#define DS 3
#define E_ 4
#define F_ 5
#define FS 6
#define G_ 7
#define GS 8
#define A_ 9
#define AS 10
#define B_ 11
#define MKNOTE(oct, note, frac) ((Note){(oct)*12+(note),1.0/(frac)})
typedef struct {
int note;
float duration;
} Note;
// ------ EXAMPLE ------
Note SUPER_MARIO_THEME_SONG[] = {
MKNOTE(4, E_, 4),
MKNOTE(4, E_, 2),
MKNOTE(4, E_, 2),
MKNOTE(4, C_, 4),
MKNOTE(4, E_, 2),
MKNOTE(4, G_, 1),
MKNOTE(3, G_, 1),
MKNOTE(4, C_, 1.5),
MKNOTE(3, G_, 1.5),
MKNOTE(3, E_, 2),
MKNOTE(3, A_, 2),
MKNOTE(3, B_, 2),
MKNOTE(3, AS, 4),
MKNOTE(3, A_, 2),
MKNOTE(3, G_, 4),
MKNOTE(4, E_, 4),
MKNOTE(4, G_, 4),
MKNOTE(4, A_, 2),
MKNOTE(4, F_, 4),
MKNOTE(4, G_, 2),
MKNOTE(4, E_, 2),
MKNOTE(4, C_, 4),
MKNOTE(4, D_, 4),
MKNOTE(3, B_, 1),
MKNOTE(4, C_, 1.5),
MKNOTE(3, G_, 1.5),
MKNOTE(3, E_, 2),
MKNOTE(3, A_, 2),
MKNOTE(3, B_, 2),
MKNOTE(3, AS, 4),
MKNOTE(3, A_, 2),
MKNOTE(3, G_, 4),
MKNOTE(4, E_, 4),
MKNOTE(4, G_, 4),
MKNOTE(4, A_, 2),
MKNOTE(4, F_, 4),
MKNOTE(4, G_, 2),
MKNOTE(4, E_, 2),
MKNOTE(4, C_, 4),
MKNOTE(4, D_, 4),
MKNOTE(3, B_, 1),
};
const double noteFrequencies[] = {
261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88
};
int convertNote(int noteNumber) {
int octave = noteNumber/12;
double freq = noteFrequencies[noteNumber%12]*pow(2, octave-4);
return (int)(freq + 0.5);
}
void generateWaveFile(const char* filename, Note* notes, int numNotes, int bpm) {
float bps = bpm/60.0f;
int i, j, k = 0;
int overallNumSamples = 0;
for (i = 0; i < numNotes; i++) {
overallNumSamples += (notes[i].duration/bps)*NUM_SAMPLES;
}
short waveform[overallNumSamples];
// Generate square wave
for (i = 0; i < numNotes; i++) {
int note = convertNote(notes[i].note);
float duration = notes[i].duration/2.0;
int samplesPerCycle = 44100 / note;
int numSamples = (int)(duration * 44100);
for (j = 0; j < numSamples; j++) {
if (j % samplesPerCycle < samplesPerCycle / 2) {
waveform[k++] = 32767/2;
} else {
waveform[k++] = -32767/2;
}
}
}
// Write the samples to a file in .wav format
FILE *fp = fopen(filename, "wb");
short format = 1;
short numChannels = 1;
short bitsPerSample = 16;
int byteRate = NUM_SAMPLES * numChannels * bitsPerSample / 8;
short blockAlign = numChannels * bitsPerSample / 8;
int dataSize = overallNumSamples * 2;
int chunkSize = 16;
int sampleRate = NUM_SAMPLES;
fwrite("RIFF", 4, 1, fp);
int riffSize = 4 + (8 + chunkSize) + (8 + dataSize);
fwrite(&riffSize, 4, 1, fp);
fwrite("WAVE", 4, 1, fp);
fwrite("fmt ", 4, 1, fp);
fwrite(&chunkSize, 4, 1, fp);
fwrite(&format, 2, 1, fp);
fwrite(&numChannels, 2, 1, fp);
fwrite(&sampleRate, 4, 1, fp);
fwrite(&byteRate, 4, 1, fp);
fwrite(&blockAlign, 2, 1, fp);
fwrite(&bitsPerSample, 2, 1, fp);
fwrite("data", 4, 1, fp);
fwrite(&dataSize, 4, 1, fp);
fwrite(waveform, 2, overallNumSamples, fp);
fclose(fp);
}
int main() {
int numNotes = sizeof(SUPER_MARIO_THEME_SONG) / sizeof(SUPER_MARIO_THEME_SONG[0]);
generateWaveFile("mario.wav", SUPER_MARIO_THEME_SONG, numNotes, 108);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment