Last active
August 29, 2015 14:02
-
-
Save stylemistake/e54e42b65ce820367b7f to your computer and use it in GitHub Desktop.
C Juke
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <math.h> | |
/* | |
Quick compile: | |
gcc juke.c -Ofast -lm -o juke && ./juke > juke.wav | |
*/ | |
#define pat(x,y) (x)[bar%(y)] | |
/* | |
IV V VI | |
--------------- | |
C 0 12 24 | |
C# 1 13 25 | |
D 2 14 26 | |
D# 3 15 27 | |
E 4 16 28 | |
F 5 17 29 | |
F# 6 18 30 | |
G 7 19 31 | |
G# 8 20 32 | |
A 9 21 33 | |
A# 10 22 34 | |
B 11 23 35 | |
*/ | |
// ---------------------------------------------------------- | |
#define RATE 44100 | |
#define BASE_NOTE 440 | |
#define NN_LIMIT -127 | |
#define NN -255 | |
int signal_sqr( int t, int note, float vel ) { | |
if ( note <= NN_LIMIT ) return 0; | |
int freq = BASE_NOTE * pow( 2, (float)(note-9)/12 ); | |
float sig = (float)t / RATE * freq; | |
return (sin( sig * M_PI * 2 ) > 0 ? 1 : -1) * vel; | |
} | |
int signal_saw( int t, int note, float vel ) { | |
if ( note <= NN_LIMIT ) return 0; | |
int freq = BASE_NOTE * pow( 2, (float)(note-9)/12 ); | |
int sig = (float)t * 0x80 / RATE * freq; | |
return ( (sig & 0xFF) - 0x80 ) * vel/0x80; | |
} | |
int signal_sin( int t, int note, float vel ) { | |
if ( note <= NN_LIMIT ) return 0; | |
int freq = BASE_NOTE * pow( 2, (float)(note-9)/12 ); | |
float sig = (float)t / RATE * freq; | |
return sin( sig * M_PI * 2 ) * vel; | |
} | |
int signal_noise( int t, float vel ) { | |
int sig = rand(); | |
return ( (sig & 0xFF) - 0x80 ) * vel/0x80; | |
} | |
// ---------------------------------------------------------- | |
int synth_tune( unsigned int t ) { | |
int melody[] = { | |
12, 17, 20, 19, NN, NN, NN, NN, | |
12, 7, 14, 15, NN, NN, NN, NN, | |
10, 14, 17, 22, NN, NN, NN, NN, | |
12, 15, 20, 24, NN, NN, NN, NN, | |
}; | |
int melody_pitch[] = { 0, 1, 0, 2 }; | |
int note = melody[ t>>13&31 ] + melody_pitch[ t>>18&3 ] * 12; | |
int vel = (-t)>>7&63; | |
return signal_saw( t, note, vel ); | |
} | |
int synth_arp( unsigned int t ) { | |
int melody[] = { | |
12, 17, 20, 7, 12, 15, | |
10, 14, 17, 12, 15, 20, | |
}; | |
int melody_pitch[] = { 0, 1, 0, 2 }; | |
int note = melody[ (t>>11)%3 + 3*(t>>16&3) ]; | |
int vel = (-t)>>7&63; | |
return signal_saw( t, note + 12, 12 ); | |
} | |
int synth_bass( unsigned int t ) { | |
int melody[] = { | |
17, 12, 19, 20, | |
17, 12, 19, 18, | |
}; | |
int note = melody[ t>>16&7 ] - 36; | |
int vel = sin( (float)t * 12 / RATE ) * 20; | |
return signal_sin( t, note, 35 ) + signal_sqr( t, note, vel ); | |
} | |
int synth_kick( unsigned int t ) { | |
int pitches[] = { | |
30, 15, 7, 4, 0, 0, 0, 0, | |
}; | |
int vel_map[] = { | |
0x50, 0x60, 0x80, 0x60, 0x50, 0x40, 0x35, 0x30, | |
}; | |
int trigger_map[] = { | |
1, 0, 0, 0, 1, 0, 0, 0, | |
1, 0, 0, 1, 0, 0, 1, 0, | |
1, 0, 0, 0, 1, 0, 0, 0, | |
1, 0, 0, 1, 0, 0, 1, 1, | |
}; | |
int note = pitches[ t>>9&7 ] - 24; | |
int vel = vel_map[ t>>9&7 ] * trigger_map[ t>>12&31 ] / 1.5; | |
return signal_sin( t, note, vel ); | |
} | |
int synth_clap( unsigned int t ) { | |
int vel_map[] = { | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0xF0, 0x80, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, | |
}; | |
int vel = vel_map[ t>>12&15 ] / 5; | |
return signal_noise( t, vel ); | |
} | |
int synth_beeps( unsigned int t ) { | |
int vel_map[] = { | |
0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0xF0, 0x00, 0xF0, 0x00, | |
}; | |
int melody_pitch[] = { 0, -9 }; | |
int note = 36 + melody_pitch[ t>>16&1 ]; | |
int vel = vel_map[ t>>11&15 ] / 10; | |
return signal_sqr( t, note, vel ); | |
} | |
int synth_closed_hat( unsigned int t ) { | |
int vel_map[] = { | |
0xF0, 0x80, 0x40, 0x20, 0x10, 0x05, 0x02, 0x01, | |
}; | |
int vel = vel_map[ t>>9&7 ] / 4; | |
return signal_noise( t, vel ); | |
} | |
int main() { | |
unsigned int t = 0; | |
int bar_mul = 18; | |
int bar_end = 20; | |
// Write RIFF header | |
int header[] = { | |
0x46464952, // RIFF | |
0x00000824, // ChunkSize | |
0x45564157, // WAVE | |
0x20746d66, // fmt_ | |
0x00000010, // Subchunk1Size | |
0x00010001, // NumChannels + PCM | |
RATE, // SampleRate | |
RATE, // ByteRate | |
0x00080001, // Bits + BlockAlign | |
0x61746164, // DATA | |
( 1 << bar_mul ) * bar_end // Subchunk2Size | |
}; | |
for ( t = 0; t < 12; t++ ) { | |
putchar( header[t] & 0xFF ); | |
putchar( header[t] >> 8 & 0xFF ); | |
putchar( header[t] >> 16 & 0xFF ); | |
putchar( header[t] >> 24 & 0xFF ); | |
} | |
for (;; t++) { | |
int sig = 0; | |
int bar = t >> 18; | |
// End of music | |
if ( bar == 20 ) break; | |
// Patterns | |
int pat_tune[] = { 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; | |
int pat_bass[] = { 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, }; | |
int pat_arp[] = { 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, }; | |
int pat_beeps[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, }; | |
int pat_kick[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, }; | |
int pat_clap[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, }; | |
int pat_chat[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, }; | |
// Instruments | |
sig += pat( pat_tune, 20 ) * synth_tune(t); | |
sig += pat( pat_bass, 20 ) * synth_bass(t); | |
sig += pat( pat_arp, 20 ) * synth_arp(t); | |
sig += pat( pat_beeps,20 ) * synth_beeps(t); | |
sig += pat( pat_clap, 20 ) * synth_clap(t); | |
sig += pat( pat_kick, 20 ) * synth_kick(t); | |
sig += pat( pat_chat, 20 ) * synth_closed_hat(t); | |
// Dirty clipping | |
if ( sig >= 0x80 ) sig = 0x79; | |
else if ( sig <= -0x80 ) sig = -0x79; | |
// Output | |
putchar( (sig+0x80) & 0xFF ); | |
} | |
// Zero padding | |
for ( t = 0; t < 256; t++ ) putchar(0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment