summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJSDurand <mmemmew@gmail.com>2021-07-11 18:45:38 +0800
committerJSDurand <mmemmew@gmail.com>2021-07-11 18:45:38 +0800
commit6c358a842baaf6d211a5c8c1717d815e7813ed96 (patch)
treec78122d49e75a540a4f6054b224a90738b554606
First commit
Now I have a kind of piano like instrument. A violin-like instrument is being constructed.
-rw-r--r--Für Elise/Für Elise notes.txt100
-rw-r--r--cat/part of cat vibing.txt118
-rw-r--r--coffin/part of coffin dance.txt87
-rw-r--r--instrument.c390
-rw-r--r--instrument.d1
-rw-r--r--instrument.h33
-rw-r--r--main.c588
-rw-r--r--main.d29
-rw-r--r--makefile96
-rw-r--r--marble machine/marble machine notes.txt5
-rw-r--r--mix.c22
-rw-r--r--mix.d1
-rw-r--r--mix.h7
l---------notes.txt1
-rw-r--r--parser.c694
-rw-r--r--parser.d1
-rw-r--r--parser.h48
-rw-r--r--test.txt3
-rw-r--r--turkish/turkish notes (archive).txt236
-rw-r--r--turkish/turkish notes.txt127
-rw-r--r--util.c80
-rw-r--r--util.d1
-rw-r--r--util.h126
23 files changed, 2794 insertions, 0 deletions
diff --git a/Für Elise/Für Elise notes.txt b/Für Elise/Für Elise notes.txt
new file mode 100644
index 0000000..cf05d9f
--- /dev/null
+++ b/Für Elise/Für Elise notes.txt
@@ -0,0 +1,100 @@
+,
+b 75 ,
+7 0.25 , s 2
+6 0.25 ,
+7 0.25 ,
+6 0.25 ,
+7 0.25 ,
+2 0.25 ,
+5 0.25 ,
+3 0.25 ,
+0 0.5 , -24 0.25
+s 0.25 , -17 0.25
+-9 0.25 , -12 0.25
+-5 0.25 , s 0.75
+0 0.25 ,
+# three bar lines
+2 0.5 , -29 0.25
+s 0.25 , -17 0.25
+-5 0.25 , -13 0.25
+-1 0.25 , s 0.75
+2 0.25 ,
+# four bar lines
+3 0.5 , -24 0.25
+s 0.25 , -17 0.25
+-5 0.25 , -12 0.25
+7 0.25 , s 0.75
+6 0.25 ,
+# five bar
+7 0.25 , s 1.5
+6 0.25 ,
+7 0.25 ,
+2 0.25 ,
+5 0.25 ,
+3 0.25 ,
+# first line over
+0 0.5 , -24 0.25
+s 0.25 , -17 0.25
+ , -12 0.25
+-9 0.25 , s 0.75
+-5 0.25 ,
+0 0.25 ,
+# one bar
+2 0.5 , -29 0.25
+s 0.25 , -17 0.25
+-9 0.25 , -13 0.25
+3 0.25 , s 0.75
+2 0.25 ,
+# two bars
+0 1 , -24 0.25
+ , -17 0.25
+ , -12 0.25
+ , s 0.25
+# repeat start
+b 120 ,
+7 0.25 , s 2
+6 0.25 ,
+7 0.25 ,
+6 0.25 ,
+7 0.25 ,
+2 0.25 ,
+5 0.25 ,
+3 0.25 ,
+0 0.5 , -24 0.25
+s 0.25 , -17 0.25
+-9 0.25 , -12 0.25
+-5 0.25 , s 0.75
+0 0.25 ,
+# three bar lines
+2 0.5 , -29 0.25
+s 0.25 , -17 0.25
+-5 0.25 , -13 0.25
+-1 0.25 , s 0.75
+2 0.25 ,
+# four bar lines
+3 0.5 , -24 0.25
+s 0.25 , -17 0.25
+-5 0.25 , -12 0.25
+7 0.25 , s 0.75
+6 0.25 ,
+# five bar
+7 0.25 , s 1.5
+6 0.25 ,
+7 0.25 ,
+2 0.25 ,
+5 0.25 ,
+3 0.25 ,
+# first line over
+0 0.5 , -24 0.25
+s 0.25 , -17 0.25
+ , -12 0.25
+-9 0.25 , s 0.75
+-5 0.25 ,
+0 0.25 ,
+# one bar
+2 0.5 , -29 0.25
+s 0.25 , -17 0.25
+-9 0.25 , -13 0.25
+3 0.25 , s 0.75
+2 0.25 ,
+# repeat over
diff --git a/cat/part of cat vibing.txt b/cat/part of cat vibing.txt
new file mode 100644
index 0000000..6b234d0
--- /dev/null
+++ b/cat/part of cat vibing.txt
@@ -0,0 +1,118 @@
+-12 0.5
+-6 0.5
+-6 0.75
+-4 0.25
+-3 0.5
+-6 0.25
+-6 0.25
+-6 0.5
+-3 0.25
+-3 0.25
+-4 0.5
+-8 0.5
+-8 0.5
+-4 0.5
+-3 0.5
+-6 0.5
+-6 0.5
+-6 0.25
+-6 0.25
+-12 0.5
+-6 0.5
+-6 0.75
+-4 0.25
+-3 0.5
+-6 0.5
+-6 0.25
+-6 0.25
+-6 0.25
+-3 0.25
+1 0.5
+1 0.25
+-3 0.25
+-3 0.5
+-4 0.5
+-3 0.5
+-6 0.5
+-6 0.5
+-6 0.25
+-6 0.25
+1 0.5
+1 0.5
+-1 0.5
+-3 0.5
+-4 0.5
+-8 0.5
+-8 0.25
+-8 0.25
+-8 0.25
+-4 0.25
+-1 0.25
+-1 0.25
+-1 0.25
+-1 0.25
+-3 0.25
+-3 0.25
+-4 0.5
+-3 0.5
+-6 0.5
+-6 0.75
+-6 0.25
+1 0.5
+1 0.5
+-1 0.5
+-3 0.5
+-4 0.5
+-8 0.5
+-8 0.25
+-8 0.25
+-8 0.25
+-4 0.25
+-1 0.25
+-1 0.25
+-1 0.25
+-1 0.25
+-3 0.5
+-4 0.5
+-3 0.5
+-6 0.5
+-6 1
+-12 0.5
+-6 0.5
+-6 0.75
+-4 0.25
+-3 0.5
+-6 0.25
+-6 0.25
+-6 0.5
+-3 0.25
+-3 0.25
+-4 0.5
+-8 0.5
+-8 0.5
+-4 0.5
+-3 0.5
+-6 0.5
+-6 0.5
+-6 0.25
+-6 0.25
+-12 0.5
+-6 0.5
+-6 0.75
+-4 0.25
+-3 0.5
+-6 0.5
+-6 0.25
+-6 0.25
+-6 0.25
+-3 0.25
+1 0.5
+1 0.25
+-3 0.25
+-3 0.5
+-4 0.5
+-3 0.5
+-6 0.5
+-6 0.5
+-6 0.25
+-6 0.25
diff --git a/coffin/part of coffin dance.txt b/coffin/part of coffin dance.txt
new file mode 100644
index 0000000..2fd051f
--- /dev/null
+++ b/coffin/part of coffin dance.txt
@@ -0,0 +1,87 @@
+1 0.5
+0 0.5
+-1 0.5
+-3 0.5
+-2 1
+-2 0.5
+2 0.5
+1 1
+0 1
+-1 1
+-1 0.5
+-1 0.5
+1 1
+0 0.5
+-1 0.5
+-2 1
+-2 0.5
+7 0.5
+6 0.5
+7 0.5
+6 0.5
+7 0.5
+-2 1
+-2 0.5
+7 0.5
+6 0.5
+7 0.5
+6 0.5
+7 0.5
+0 0.5
+0 0.5
+0 0.5
+0 0.5
+2 0.5
+2 0.5
+2 0.5
+2 0.5
+1 0.5
+1 0.5
+1 0.5
+1 0.5
+4 0.5
+4 0.5
+4 0.5
+4 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+5 0.5
+1 0.5
+0 0.5
+-1 0.5
+-3 0.5
+-2 1
+-2 0.5
+2 0.5
+1 1
+0 1
+-1 1
+-1 0.5
+-1 0.5
+1 1
+0 0.5
+-1 0.5
+-2 1
+-2 0.5
+7 0.5
+6 0.5
+7 0.5
+6 0.5
+7 0.5
+-2 1
+-2 0.5
+7 0.5
+6 0.5
+7 0.5
+6 0.5
+7 0.5
+
diff --git a/instrument.c b/instrument.c
new file mode 100644
index 0000000..edb2f76
--- /dev/null
+++ b/instrument.c
@@ -0,0 +1,390 @@
+#include "instrument.h"
+#include <math.h>
+#include <stdlib.h>
+
+/* A hard limit */
+#define REVERB_MAX 0
+
+/* This is not exposed to other files, as this should be controlled by
+ individual instruments. */
+typedef struct {
+ float freq;
+ float amp;
+ unsigned char pred; /* if this is 0, then no frequency
+ modulation is present. */
+} FM;
+
+/* By similar reasoning, ADSR is not exposed either. */
+typedef struct {
+ float attack_p;
+ float decay_p;
+ float sustain_p;
+ float release_p;
+ float sustain_level;
+ unsigned char pred; /* If this is 0 then the instrument
+ does not adapt the ADSR model. */
+} ADSR;
+
+/* NOTE: The default method for an instrument to produce sounds is in
+ accordance with its FM, ADSR, and Fourier coefficients. But
+ individual instruments can override that default method by
+ providing their own functions. This way we would have more
+ flexibility. */
+
+struct Instrument_s {
+ float *sin_coefs; /* coefficients of sin in the Fourier
+ expansion. */
+ float *cos_coefs; /* coefficients of cos in the Fourier
+ expansion. */
+ sound_t sound; /* the function to produce sounds. */
+ merger_t merger; /* the function to merge waves. */
+ float reverb; /* The percentage of reverberation. */
+ FM fm;
+ ADSR adsr;
+};
+
+WaveFrag
+make_sound(Instrument * in, Volume v, Hertz h, Seconds s)
+{
+ return in->sound(in, v, h, s);
+}
+
+U_ATTR
+WaveFrag
+merge_waves(Instrument *in, WaveFrag *frags, LENT len, int step)
+{
+ return in->merger(in, frags, len, step);
+}
+
+/* Now comes various instruments */
+
+/* The first is piano */
+
+UH_ATTR
+static float
+smooth_step(float t)
+{
+ if (t <= 0.0f) {
+ return 0.0f;
+ } else if (t >= 1.0f) {
+ return 1.0f;
+ } else {
+ return 6*pow(t, 5)-15*pow(t, 4)+10*pow(t, 3);
+ }
+}
+
+UH_ATTR
+static float
+old_piano_sound_internal(Instrument *piano, float theta)
+{
+ return sin(theta)+1.869*sin(2.0f*theta)+0.042f*sin(3.0f*theta)
+ +0.022f*sin(4.0f*theta)+cos(theta);
+}
+
+UH_ATTR
+static float
+piano_sound_internal(Instrument *piano, float theta)
+{
+ return (*(piano->sin_coefs) * sin(theta)+
+ *(piano->sin_coefs+1) * sin(2.0f*theta)+
+ *(piano->sin_coefs+2) * sin(3.0f*theta))*
+ exp(-0.004f*theta);
+}
+
+UH_ATTR
+WaveFrag
+piano_sound(void * in, Volume v, Hertz h, Seconds s)
+{
+ Instrument *piano = (Instrument *)in;
+
+ float step = (float) (h * 2 * M_PI) / SAMPLE_RATE;
+ LENT sample_num = (LENT) floor(SAMPLE_RATE * s);
+
+ Wave w = MYALLOC(Pulse, sizeof(*w) * sample_num);
+
+ float theta = 0.0f, temp = 0.0f;
+
+ for (LENT i = 0; i < sample_num; i++, theta += step) {
+ temp = piano_sound_internal(piano, theta);
+ temp += temp*temp*temp;
+ *(w+i) = (float) v * piano_sound_internal(piano, temp);
+ }
+
+ return (WaveFrag) { w, sample_num };
+}
+
+/* Just concatenates waves together. */
+UH_ATTR
+WaveFrag
+simple_merger(void *in, WaveFrag *frags, LENT len, int step)
+{
+ WaveFrag wf;
+
+ LENT total_len = 0;
+
+ for (LENT i = 0; i < len; i += step)
+ total_len += (frags+i)->n;
+
+ wf.n = total_len;
+ wf.w = MYALLOC(Pulse, total_len);
+
+ for (LENT i = 0, counter = 0, local = 0;
+ i < len && counter < total_len;
+ i += step) {
+ local = (frags+i)->n;
+
+ for (LENT j = 0; j < local;)
+ *(wf.w+counter++) = *((frags+i)->w+j++);
+
+ free((frags+i)->w);
+ }
+
+ free(frags);
+
+ return wf;
+}
+
+Instrument *make_piano()
+{
+ Instrument *ins = malloc(sizeof *ins * 1);
+
+ /* The piano's sound is simulated directly by some coefficients. */
+ ins->fm.pred = 0;
+ ins->adsr.pred = 0;
+ ins->sin_coefs = malloc(sizeof(*(ins->sin_coefs)) * 3);
+ ins->cos_coefs = malloc(sizeof(*(ins->cos_coefs)) * 1);
+ ins->reverb = 0.0f;
+
+ *(ins->sin_coefs) = 0.65f;
+ *(ins->sin_coefs+1) = 0.45f;
+ *(ins->sin_coefs+2) = 0.1f;
+ *(ins->cos_coefs) = 1;
+ ins->sound = piano_sound;
+ ins->merger = simple_merger;
+
+ return ins;
+}
+
+void
+destroy_piano(Instrument *in)
+{
+ free(in->sin_coefs);
+ free(in->cos_coefs);
+ free(in);
+}
+
+/* Then comes violin */
+
+UH_ATTR
+Pulse
+violin_sound_internal(Instrument *in, float theta)
+{
+ /* return -1.0f*fmod(theta, 2.0f*M_PI)/M_PI + 1.0f; */
+ /* return (*(in->sin_coefs)*sin(theta)+
+ * *(in->sin_coefs+1)*sin(2.0f*theta)+
+ * *(in->sin_coefs+2)*sin(3.0f*theta)+
+ * *(in->sin_coefs+3)*sin(4.0f*theta)+
+ * *(in->sin_coefs+4)*sin(5.0f*theta)); */
+ Pulse result = 0.0f;
+ for (int i = 0; i < 26; i++)
+ result += *(in->sin_coefs+i) * sin((float) (i+1)*theta);
+ /* result = sin(theta); */
+
+ return result;
+}
+
+UH_ATTR
+WaveFrag
+violin_sound(void * in, Volume v, Hertz h, Seconds s)
+{
+ Instrument *violin = (Instrument *)in;
+
+ /* We produce a little more samples so that we can create the effect
+ of reverberation later on. */
+ s *= (1.0f+violin->reverb);
+
+ float step = (float) (h * 2 * M_PI) / SAMPLE_RATE;
+ LENT sample_num = (LENT) floor(SAMPLE_RATE * s);
+ float attack_step = 1.0f / (float) (sample_num * violin->adsr.attack_p);
+ float decay_step = 1.0f / (float) (sample_num * violin->adsr.decay_p);
+ float sustain_step = 1.0f / (float) (sample_num * violin->adsr.sustain_p);
+ float release_step = 1.0f / (float) (sample_num * violin->adsr.release_p);
+
+ Wave w = MYALLOC(Pulse, sizeof(*w) * sample_num);
+
+ float theta = 0.0f, temp = 0.0f, adsrcounter = 0.0f;
+
+ unsigned char phase = 0, sustain_flag = 0;
+
+ for (LENT i = 0; i < sample_num; i++, theta += step) {
+ temp = violin_sound_internal(violin, theta);
+ /* temp += temp*temp*temp; */
+ /* *(w+i) = (float) v * temp * ((sustain_flag) ? SUSTAIN_LEVEL : adsrcounter); */
+ *(w+i) = (float) v * temp;
+
+ switch (phase) {
+ case 0: /* attack phase */
+ adsrcounter += attack_step;
+ if (adsrcounter >= 1.0f) {
+ adsrcounter = 1.0f;
+ phase++;
+ }
+ break;
+ case 1: /* decay phase */
+ adsrcounter -= decay_step;
+ if (adsrcounter <= SUSTAIN_LEVEL) {
+ adsrcounter = 0.0f;
+ sustain_flag = 1;
+ phase++;
+ }
+ break;
+ case 2: /* sustain phase */
+ adsrcounter += sustain_step;
+ if (adsrcounter >= 1.0f) {
+ adsrcounter = SUSTAIN_LEVEL;
+ sustain_flag = 0;
+ phase++;
+ }
+ break;
+ default: /* release phase */
+ adsrcounter -= release_step;
+ if (adsrcounter <= 0.0f) adsrcounter = 0.0f;
+ break;
+ }
+ }
+
+ return (WaveFrag) { w, sample_num };
+}
+
+UH_ATTR
+WaveFrag
+violin_merger(void *in, WaveFrag *frags, LENT len, int step)
+{
+ Instrument *violin = (Instrument*) in;
+
+ WaveFrag wf, local_last_wave = (WaveFrag) { NULL, 0 };
+ WaveFrag local_current_wave = (WaveFrag) { NULL, 0 };
+ WaveFrag local_mix_wave, local_mix_input[2];
+
+ LENT total_len = 0;
+
+ float reverb_ratio = 1.0f / (1.0f + violin->reverb);
+
+ for (LENT i = 0; i < len; i += step)
+ total_len += (LENT) ((frags+i)->n * reverb_ratio);
+
+ wf.n = total_len;
+ wf.w = MYALLOC(Pulse, total_len);
+
+ for (LENT i = 0, counter = 0, local = 0, local_last = 0;
+ i < len && counter < total_len;
+ i += step) {
+ /* The last fragment that we want to mix. */
+ local_last = (!i) ? 0 : (frags+i-1)->n - local;
+ local = (LENT) ((frags+i)->n * reverb_ratio);
+ /* Make sure everything stays within bounds. */
+ local_last = min(local_last, local);
+ local_last = min(local_last, REVERB_MAX);
+
+ if (local_last && i) {
+ /* If we reach here then i > 0. */
+ local_last_wave.n = local_last;
+ local_last_wave.w = realloc(local_last_wave.w,
+ sizeof(Pulse) * local_last);
+
+ local_current_wave.n = local_last;
+ local_current_wave.w = realloc(local_current_wave.w,
+ sizeof(Pulse) * local_last);
+
+ for (LENT j = 0; j < local_last; j++) {
+ *(local_last_wave.w+j) =
+ *((frags+i-1)->w+(frags+i-1)->n-local_last+j);
+
+ *(local_current_wave.w+j) = *((frags+i)->w+j);
+ }
+
+ local_mix_input[0] = local_last_wave;
+ local_mix_input[1] = local_current_wave;
+
+ local_mix_wave = mix_waves(local_mix_input, 2);
+
+ for (LENT j = 0; j < local_last;)
+ *(wf.w+counter++) = *(local_mix_wave.w+j++);
+
+ free(local_mix_wave.w);
+
+ for (LENT j = local_last; j < local;)
+ *(wf.w+counter++) = *((frags+i)->w+j++);
+ } else {
+ for (LENT j = 0; j < local;)
+ *(wf.w+counter++) = *((frags+i)->w+j++);
+ }
+
+ free((frags+i)->w);
+ }
+
+ free(local_last_wave.w);
+ free(local_current_wave.w);
+
+ free(frags);
+
+ return wf;
+}
+
+UH_ATTR
+Instrument *
+make_violin()
+{
+ Instrument * violin= malloc(sizeof *violin);
+
+ violin->fm.pred = 0;
+ violin->adsr.pred = 1;
+
+ violin->reverb = 0.01f;
+ violin->adsr.attack_p = 0.55;
+ violin->adsr.release_p = 0.25;
+ violin->adsr.decay_p = 0.05;
+ violin->adsr.sustain_p = max(1.0f - (violin->adsr.attack_p+
+ violin->adsr.decay_p+
+ violin->adsr.release_p),
+ 0.0f);
+ violin->adsr.sustain_level = 0.9;
+
+ double temp [] = {
+ 1.0, 0.399064778, 0.229404484, 0.151836061,
+ 0.196754229, 0.093742264, 0.060871957,
+ 0.138605419, 0.010535002, 0.071021868,
+ 0.029954614, 0.051299684, 0.055948288,
+ 0.066208224, 0.010067391, 0.00753679,
+ 0.008196947, 0.012955577, 0.007316738,
+ 0.006216476, 0.005116215, 0.006243983,
+ 0.002860679, 0.002558108, 0.0, 0.001650392
+ };
+
+ violin->sin_coefs = malloc(sizeof(float)*26);
+ for (int i = 0; i < 26; i++)
+ *(violin->sin_coefs+i) = *(temp+i);
+ /* *(violin->sin_coefs) = 1.0f;
+ * *(violin->sin_coefs+1) = 0.41f;
+ * *(violin->sin_coefs+2) = 0.35f;
+ * *(violin->sin_coefs+3) = 0.39f;
+ * *(violin->sin_coefs+4) = 0.45f;
+ * *(violin->sin_coefs+5) = 0.35f;
+ * *(violin->sin_coefs+6) = 0.5f;
+ * *(violin->sin_coefs+7) = 0.4f;
+ * *(violin->sin_coefs+8) = 0.35f;
+ * *(violin->sin_coefs+9) = 0.1f; */
+
+ violin->sound = violin_sound;
+ violin->merger = simple_merger;
+
+ return violin;
+}
+
+UH_ATTR
+void
+destroy_violin(Instrument *in)
+{
+ free(in->sin_coefs);
+ free(in);
+}
diff --git a/instrument.d b/instrument.d
new file mode 100644
index 0000000..3f8041b
--- /dev/null
+++ b/instrument.d
@@ -0,0 +1 @@
+instrument.o instrument.d : instrument.c instrument.h util.h
diff --git a/instrument.h b/instrument.h
new file mode 100644
index 0000000..0ab12c9
--- /dev/null
+++ b/instrument.h
@@ -0,0 +1,33 @@
+#ifndef INSTRUMENT_H
+#define INSTRUMENT_H
+#include "util.h"
+
+struct Instrument_s;
+
+typedef struct Instrument_s Instrument;
+
+typedef WaveFrag (*sound_t)(void *, Volume v, Hertz h, Seconds s);
+typedef WaveFrag (*merger_t)(void *, WaveFrag *, LENT, int);
+
+WaveFrag make_sound(Instrument * in, Volume v, Hertz h, Seconds s);
+
+WaveFrag piano_sound(void * in, Volume v, Hertz h, Seconds s);
+
+/* Need this so that we can let instruments control reverberation,
+ say. */
+
+U_ATTR WaveFrag merge_waves(Instrument *in, WaveFrag *frags, LENT len, int step);
+
+/* Various instruments */
+
+Instrument *make_piano();
+
+void destroy_piano(Instrument *in);
+
+/* Violin */
+
+Instrument * make_violin();
+
+void destroy_violin(Instrument *in);
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..43bf52b
--- /dev/null
+++ b/main.c
@@ -0,0 +1,588 @@
+/* This is a program that can read in a text file and convert it to a
+ sound file. Since this is going to depend on ffmpeg's libraries,
+ otherwise I am only able to produce raw audio files, which are too
+ large and not very convenient to use, a makefile is included to
+ make the compiling process easier. */
+
+/* include stuff */
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+/* include FFMPEG stuff */
+#include <libavcodec/avcodec.h>
+
+#include <libavutil/channel_layout.h>
+#include <libavutil/common.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+
+/* Self-Dependencies */
+#include "util.h"
+#include "instrument.h"
+#include "parser.h"
+
+/* #include <libavformat/avformat.h> */
+
+/* helper functions */
+
+/* FFMPEG helpers */
+
+/* check that a given sample format is supported by the encoder */
+UNUSED
+static int
+check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
+{
+ const enum AVSampleFormat *p = codec->sample_fmts;
+
+ /* the array is terminated by -1 */
+ for (int i = 0; *(p+i) != -1; i++)
+ fprintf(stderr, "supports %s\n", av_get_sample_fmt_name(*(p+i)));
+
+ while (*p != AV_SAMPLE_FMT_NONE) {
+ if (*p == sample_fmt)
+ return 1;
+ p++;
+ }
+ return 0;
+}
+
+__attribute__((__hot__, __unused__))
+static void
+/* encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt,
+ * FILE *output) */
+encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *output)
+{
+ int ret = 0;
+
+ /* send the frame for encoding */
+ ret = avcodec_send_frame(ctx, frame);
+ if (ret < 0) {
+ fprintf(stderr, "Error sending the frame to the encoder\n");
+ exit(1);
+ }
+
+ /* read all the available output packets (in general there may be any
+ * number of them */
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(ctx, pkt);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ return;
+ else if (ret < 0) {
+ fprintf(stderr, "Error encoding audio frame\n");
+ exit(1);
+ }
+
+ fwrite(pkt->data, 1, pkt->size, output);
+ /* av_write_frame(afc, pkt); */
+ av_packet_unref(pkt);
+ }
+}
+
+/* select layout with the highest channel count */
+UNUSED
+static int
+select_channel_layout(const AVCodec *codec)
+{
+ const uint64_t *p;
+ uint64_t best_ch_layout = 0;
+ int best_nb_channels = 0;
+
+ if (!codec->channel_layouts)
+ return AV_CH_LAYOUT_STEREO;
+
+ p = codec->channel_layouts;
+ while (*p) {
+ int nb_channels = av_get_channel_layout_nb_channels(*p);
+
+ if (nb_channels > best_nb_channels) {
+ best_ch_layout = *p;
+ best_nb_channels = nb_channels;
+ }
+ p++;
+ }
+ return best_ch_layout;
+}
+
+UHA_ATTR
+static inline Pulse
+square_wave(float counter)
+{
+ return (fmod(counter, 2*M_PI) < M_PI) ? 1 : -1;
+}
+
+UHA_ATTR
+static inline Pulse
+synthesize_wave(float theta)
+{
+
+ /* return -1.0f + fmod(theta, 2*M_PI)/M_PI; */
+
+ /* return sin(theta); */
+
+ /* return asin(sin(theta)) * 2/M_PI; */
+
+ /* return pow(theta/2*M_PI, 2); */
+
+ /* return sin(theta)+0.442f*sin(2.0f*theta)+0.315f*sin(3.0f*theta)
+ * +0.083f*sin(4.0f*theta)+cos(theta)+0.442f*cos(2.0f*theta); */
+
+ /* piano like */
+
+ /* return sin(theta)+1.869*sin(2.0f*theta)+0.042f*sin(3.0f*theta)
+ * +0.022f*sin(4.0f*theta)+cos(theta); */
+
+ /* excellent piano like */
+
+ return ((0.65f*sin(theta)+0.5f*sin(2.0f*theta))*exp(-0.004f*theta));
+
+}
+
+/* This is replaced by make_sound function. */
+/* UH_ATTR
+ * static inline WaveFrag
+ * oscillator(Volume v, Hertz h, Seconds s, Instrument *ins)
+ * {
+ *
+ * return 0.0f;
+ * } */
+
+UHA_ATTR
+static inline float
+frequency_modulation(float theta, Hertz h, float fm)
+{
+ return theta+FM_AMP*theta*sin(fm);
+}
+
+UH_ATTR
+static WaveFrag
+hstow(Volume v, Hertz h, Seconds s)
+{
+ LENT sample_num = (LENT) floor(SAMPLE_RATE * s);
+ float attack_step = 1.0f / (float) (sample_num * ATTACK_P);
+ float decay_step = 1.0f / (float) (sample_num * DECAY_P);
+ float sustain_step = 1.0f / (float) (sample_num * SUSTAIN_P);
+ float release_step = 1.0f / (float) (sample_num * RELEASE_P);
+
+ float step = (float) (h * 2 * M_PI) / SAMPLE_RATE;
+
+ float fm_step = (float) ((FM_FREQUENCY)*2*M_PI) / SAMPLE_RATE;
+
+ unsigned char phase = 0, sustain_flag = 0;
+
+ Wave w = MYALLOC(Pulse, sizeof(*w) * sample_num);
+
+ float adsrcounter = 0.0f, counter = 0.0f, fm_counter = 0.0f;
+ float theta = 0.0f, temp = 0.0f;
+
+ for (LENT i = 0; i < sample_num; i++, counter += step, fm_counter += fm_step) {
+ /* theta = frequency_modulation(counter, (float) h*i/((float) SAMPLE_RATE), fm_counter); */
+ theta=counter;
+ temp = synthesize_wave(theta);
+ temp += temp*temp*temp;
+ temp = synthesize_wave(temp);
+ temp += theta*exp(-2.0f*theta);
+ *(w+i) = (float) v * temp;
+ /* (synthesize_wave(temp)/\* +
+ * * 0.01*(2.0f*(float)rand()/(float) RAND_MAX - 1.0f) *\/) *//* *
+ * ((sustain_flag) ? SUSTAIN_LEVEL : adsrcounter) ;*/
+ switch (phase) {
+ case 0: /* attack phase */
+ adsrcounter += attack_step;
+ if (adsrcounter >= 1.0f) {
+ adsrcounter = 1.0f;
+ phase++;
+ }
+ break;
+ case 1: /* decay phase */
+ adsrcounter -= decay_step;
+ if (adsrcounter <= SUSTAIN_LEVEL) {
+ adsrcounter = 0.0f;
+ sustain_flag = 1;
+ phase++;
+ }
+ break;
+ case 2: /* sustain phase */
+ adsrcounter += sustain_step;
+ if (adsrcounter >= 1.0f) {
+ adsrcounter = SUSTAIN_LEVEL;
+ sustain_flag = 0;
+ phase++;
+ }
+ break;
+ default: /* release phase */
+ adsrcounter -= release_step;
+ if (adsrcounter <= 0.0f) adsrcounter = 0.0f;
+ break;
+ }
+ }
+
+ return (WaveFrag) { w, sample_num };
+}
+
+H_ATTR
+static WaveFrag
+sbtow(Volume v, Semitones st, Beats b)
+{
+ return hstow(v, stoh(st), (b * BEAT_DUR));
+}
+
+/* compose */
+
+D_ATTR("play_sheet")
+static WaveFrag
+compose(Volume *vs, Semitones *sts, Beats *bs, LENT len)
+{
+ WaveFrag wf, *temp = MYALLOC(WaveFrag, len);
+
+ LENT total_len = 0;
+
+ for (LENT i = 0; i < len; i++) {
+ if (*(sts+i) >= -50)
+ *(temp+i) = sbtow(*(vs+i), *(sts+i), *(bs+i));
+ else
+ *(temp+i) = sbtow(*(vs+i), *(sts+i), *(bs+i));
+ total_len += (temp+i)->n;
+ }
+
+ wf.n = total_len;
+
+ wf.w = MYALLOC(Pulse, total_len);
+
+ LENT counter = 0;
+
+ for (int i = 0; i < len; i++) {
+ LENT local = (temp+i)->n;
+
+ for (LENT j = 0; j < local; j++)
+ *(wf.w+counter++) = *((temp+i)->w+j);
+
+ free((temp+i)->w);
+ }
+
+ free(temp);
+
+ return wf;
+}
+
+/* Reading */
+
+/* The file should contain an even number of numbers. For any natural
+ number N, the 2*N th number specifies the Semitone of the Nth note,
+ and the 2*N+1 number specifies the beats of the Nth note. */
+
+U_ATTR
+static void
+read_sb(const char *str, int str_len,
+ int *len, Semitones **sts, Beats **bs)
+{
+ int str_counter = 0;
+
+ float temp = 0;
+
+ int semi_len = 0, b_len = 0;
+ Node_t semis = EMPTY_NODE, beats = EMPTY_NODE;
+ Node_t sn = EMPTY_NODE, bn = EMPTY_NODE;
+ Node_t *sp = NULL, *bp = NULL;
+
+ LENT nth = 0;
+
+ for (;str_counter < str_len; nth++) {
+ int number_chars_read = 0;
+ int result = sscanf(str+str_counter, "%f %n", &temp, &number_chars_read);
+ if (result < 1) break;
+
+ str_counter += number_chars_read;
+
+ if (nth % 2) {
+ /* odd */
+ PUSH_NODE(b_len, bp, beats, temp, f);
+ } else {
+ /* even */
+ PUSH_NODE(semi_len, sp, semis, temp, f);
+ }
+ }
+
+ if (nth % 2) {
+ fprintf(stderr, "There should be an even number of floats in "
+ "the file, but found %lu.\n",
+ nth);
+ exit(1);
+ }
+
+ *len = floor(nth/2.0f);
+ *sts = realloc(*sts, sizeof(float) * (*len));
+ *bs = realloc(*bs, sizeof(float) * (*len));
+
+ for (int i = 0; i < *len; i++) {
+ POP_NODE(semi_len, temp, semis, sn, f);
+ *(*sts+*len-1-i) = temp;
+
+ POP_NODE(b_len, temp, beats, bn, f);
+ *(*bs+*len-1-i) = temp;
+ }
+}
+
+/* main */
+
+int main(int argc, char **argv)
+{
+ const AVCodec *codec;
+ AVCodecContext *c = NULL;
+ /* AVOutputFormat *of;
+ * AVFormatContext *oc; */
+ AVFrame *frame;
+ AVPacket *pkt;
+ int ret = 0;
+ float *samples;
+
+ /* output format setup */
+
+ /* if (!(of = av_guess_format(NULL, DEFAULT_OUTPUT_NAME, "audio/aac"))) {
+ * fprintf(stderr, "cannot guess output format\n");
+ * exit(1);
+ * } */
+
+ /* avformat_alloc_output_context2(&oc, of, NULL, NULL); */
+ /* oc = avformat_alloc_context();
+ *
+ * if (!oc) {
+ * fprintf(stderr, "cannot allocate output context\n");
+ * exit(1);
+ * } */
+
+ /* oc->oformat = of; */
+ /* oc->url = MYALLOC(char, sizeof(DEFAULT_OUTPUT_NAME));
+ * for (int i = 0; i < sizeof(DEFAULT_OUTPUT_NAME); i++)
+ * oc->url[i] = DEFAULT_OUTPUT_NAME[i]; */
+
+ /* ret = avio_open(&oc->pb, DEFAULT_OUTPUT_NAME, AVIO_FLAG_READ_WRITE);
+ *
+ * if (ret<0) {
+ * fprintf(stderr, "cannot open %s\n", DEFAULT_OUTPUT_NAME);
+ * exit(1);
+ * } */
+
+ /* find the encoder */
+ codec = avcodec_find_encoder(AV_CODEC_ID_MP3);
+ if (!codec) {
+ fprintf(stderr, "Codec not found\n");
+ exit(1);
+ }
+
+ /* set an option to place MOOV atom first. */
+
+ /* AVDictionary *opt = NULL;
+ * av_dict_set(&opt, "movflags", "faststart", 0);
+ *
+ * printf("of->audio_codec = %d\n", of->audio_codec);
+ *
+ * if (oc->oformat->flags & AVFMT_GLOBALHEADER)
+ * c->flags |= AVFMT_GLOBALHEADER; */
+
+ /* return 0; */
+
+ /* STD_BASE = (float) pow(2, 1.0f/12.0f); */
+
+ UNUSED int notes_len = 0;
+
+ /* UNUSED Volume *vs = NULL;
+ * Semitones *sts = MYALLOC(Semitones, 1);
+ * Beats *bs = MYALLOC(Beats, 1); */
+
+ char *notes_content = MYALLOC(char, 500);
+
+ LENT notes_str_len = 0;
+
+ notes_str_len = (LENT) read_entire_file("notes.txt", &notes_content);
+
+#ifdef DEBUG
+ fprintf(stderr, "notes content = %s\n", notes_content);
+#endif
+
+ PSheet psh = read_sheet(notes_content, notes_str_len);
+
+ if (argc >= 2 && **(argv+1) == 'p')
+ print_sheet(psh);
+
+#ifdef DEBUG
+ fprintf(stderr, "after printing\n");
+#endif
+
+ /* read_sb(notes_content, notes_str_len, &notes_len,
+ * &sts, &bs); */
+
+#ifdef DEBUG
+ fprintf(stderr, "notes len = %d\nfirst bs = %.2f\n",
+ notes_len, *bs);
+#endif
+
+ /* vs = MYALLOC(Volume, notes_len);
+ * for (int i = 0; i < notes_len;) *(vs+i++) = 0.3f; */
+
+ /* WaveFrag wf = compose(vs, sts, bs, notes_len); */
+ WaveFrag wf = play_sheet(psh, 0.3f);
+
+ destroy_sheet(psh);
+
+ printf("wf n = %lu\n", wf.n);
+
+ /* AVStream *os = avformat_new_stream(oc, NULL); */
+
+ c = avcodec_alloc_context3(codec);
+ if (!c) {
+ fprintf(stderr, "Could not allocate audio codec context\n");
+ exit(1);
+ }
+
+ /* check that the encoder supports FLTP pcm input */
+ /* The P marks that this is "planar", i.e. the input is not
+ interleaved between channels. */
+ c->sample_fmt = AV_SAMPLE_FMT_FLTP;
+ if (!check_sample_fmt(codec, c->sample_fmt)) {
+ fprintf(stderr, "Encoder does not support sample format %s",
+ av_get_sample_fmt_name(c->sample_fmt));
+ exit(1);
+ }
+
+ /* set other audio parameters supported by the encoder */
+ c->sample_rate = (int) SAMPLE_RATE;
+ c->channel_layout = select_channel_layout(codec);
+ c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
+
+ /* this should be sizeof(float) * 8 * nb_channels * SAMPLE_RATE
+
+ We divide by 1000 since the unit is KB.*/
+ c->bit_rate = sizeof(float) * 8 * SAMPLE_RATE * c->channels / 1000;
+
+ /* os->time_base = (AVRational) { 1, (int) SAMPLE_RATE }; */
+
+ /* open it */
+ if (avcodec_open2(c, codec, NULL) < 0) {
+ fprintf(stderr, "Could not open codec\n");
+ exit(1);
+ }
+
+ /* /\* set stream data *\/
+ * os->codecpar->sample_rate = SAMPLE_RATE;
+ * os->codecpar->format = c->sample_fmt;
+ * os->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ * os->codecpar->codec_id = of->audio_codec;
+ * os->codecpar->bit_rate = c->bit_rate;
+ * os->codecpar->channels = c->channels;
+ * os->codecpar->channel_layout = c->channel_layout;
+ * os->codecpar->frame_size = c->frame_size;
+ *
+ * if (avformat_write_header(oc, &opt) < 0) {
+ * fprintf(stderr, "cannot write header\n");
+ * exit(1);
+ * }
+ *
+ * av_dump_format(oc, 0, DEFAULT_OUTPUT_NAME, 1); */
+
+ /* return 0; */
+
+ /* audio_st.st->time_base = (AVRational){ 1, c->sample_rate };
+ *
+ * if (oc->oformat->flags & AVFMT_GLOBALHEADER)
+ * c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; */
+
+ /* av_dump_format(oc, 0, DEFAULT_OUTPUT_NAME, 1); */
+
+ /* ret = avio_open(&oc->pb, DEFAULT_OUTPUT_NAME, AVIO_FLAG_WRITE);
+ * if (ret < 0) {
+ * fprintf(stderr, "Could not open '%s': %s\n", DEFAULT_OUTPUT_NAME,
+ * av_err2str(ret));
+ * return 1;
+ * }
+ *
+ * ret = avformat_write_header(oc, &opt);
+ * if (ret < 0) {
+ * fprintf(stderr, "Error occurred when opening output file: %s\n",
+ * av_err2str(ret));
+ * return 1;
+ * } */
+
+ printf("sample rate = %d, channels = %d, bit rate = %lu\n"
+ "frame size = %d\n",
+ c->sample_rate, c->channels, (long) c->bit_rate,
+ c->frame_size);
+ /* return 0; */
+
+ FILE *file = fopen(DEFAULT_OUTPUT_NAME, "wb");
+ if (!file) {
+ fprintf(stderr, "Could not open %s\n", DEFAULT_OUTPUT_NAME);
+ exit(1);
+ }
+
+ /* packet for holding encoded output */
+ pkt = av_packet_alloc();
+ if (!pkt) {
+ fprintf(stderr, "could not allocate the packet\n");
+ exit(1);
+ }
+
+ /* pkt->stream_index = os->index; */
+
+ /* frame containing input raw audio */
+ frame = av_frame_alloc();
+ if (!frame) {
+ fprintf(stderr, "Could not allocate audio frame\n");
+ exit(1);
+ }
+
+ frame->nb_samples = c->frame_size;
+ frame->format = c->sample_fmt;
+ frame->channel_layout = c->channel_layout;
+
+ /* allocate the data buffers */
+ ret = av_frame_get_buffer(frame, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Could not allocate audio data buffers\n");
+ fprintf(stderr, "nbs = %d, format = %s, layout = %d\n",
+ frame->nb_samples, av_get_sample_fmt_name(frame->format),
+ av_get_channel_layout_nb_channels(frame->channel_layout));
+ exit(1);
+ }
+
+ /* write to the frame */
+
+ long frame_num = floor((float) wf.n/(float) c->frame_size) + 1;
+ long w_counter = 0;
+
+ for (int i = 0; i < frame_num; i++, w_counter += c->frame_size) {
+ ret = av_frame_make_writable(frame);
+ if (ret < 0) {
+ fprintf(stderr, "cannot make frame writable\n");
+ exit(1);
+ }
+
+ for (int j = 0; j < c->channels; j++) {
+ samples = (float *)frame->data[j];
+
+ for (int k = 0; k < c->frame_size && k+w_counter < wf.n; k++)
+ samples[k] = *(wf.w+w_counter+k);
+ }
+
+ encode(c, frame, pkt, file);
+ }
+
+ /* fwrite(wf.w, sizeof(float), wf.n, file); */
+
+ /* flush the encoder */
+ encode(c, NULL, pkt, file);
+
+ /* free(vs); */
+ /* free(sts);
+ * free(bs); */
+ free(wf.w);
+ free(notes_content);
+ fclose(file);
+
+ av_frame_free(&frame);
+ av_packet_free(&pkt);
+ avcodec_free_context(&c);
+ /* av_dict_free(&opt); */
+
+ return 0;
+}
diff --git a/main.d b/main.d
new file mode 100644
index 0000000..51047cb
--- /dev/null
+++ b/main.d
@@ -0,0 +1,29 @@
+main.o main.d : main.c /usr/local/include/libavcodec/avcodec.h \
+ /usr/local/include/libavutil/samplefmt.h \
+ /usr/local/include/libavutil/avutil.h \
+ /usr/local/include/libavutil/common.h \
+ /usr/local/include/libavutil/attributes.h \
+ /usr/local/include/libavutil/macros.h \
+ /usr/local/include/libavutil/version.h \
+ /usr/local/include/libavutil/avconfig.h \
+ /usr/local/include/libavutil/mem.h \
+ /usr/local/include/libavutil/error.h \
+ /usr/local/include/libavutil/rational.h \
+ /usr/local/include/libavutil/mathematics.h \
+ /usr/local/include/libavutil/intfloat.h \
+ /usr/local/include/libavutil/log.h \
+ /usr/local/include/libavutil/pixfmt.h \
+ /usr/local/include/libavutil/buffer.h \
+ /usr/local/include/libavutil/cpu.h \
+ /usr/local/include/libavutil/channel_layout.h \
+ /usr/local/include/libavutil/dict.h \
+ /usr/local/include/libavutil/frame.h \
+ /usr/local/include/libavutil/hwcontext.h \
+ /usr/local/include/libavcodec/bsf.h \
+ /usr/local/include/libavcodec/codec_id.h \
+ /usr/local/include/libavcodec/codec_par.h \
+ /usr/local/include/libavcodec/packet.h \
+ /usr/local/include/libavcodec/version.h \
+ /usr/local/include/libavcodec/codec.h \
+ /usr/local/include/libavcodec/codec_desc.h util.h instrument.h \
+ parser.h
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..fd11a83
--- /dev/null
+++ b/makefile
@@ -0,0 +1,96 @@
+CC=gcc
+CPPFLAGS= -Wall -c # -I
+LIBS= -Wall -O2 -L /usr/local/lib -lavutil -lavcodec -lavformat # -L /Users/durand/Desktop/Centre/A\ propos\ de\ programmes/C/player/lib -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -L /usr/local/lib -lavutil -lswscale -lavcodec
+out=output.mp3
+SOURCES=main.c instrument.c util.c parser.c mix.c
+
+all: main TAGS
+
+%.d: %.c
+ @echo "Remaking makefiles..."
+ @set -e; rm -f $@; \
+ $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+ @echo "Done"
+
+include $(SOURCES:.c=.d)
+
+TAGS: *.c *.h
+ etags $+
+
+parser.o : parser.c parser.h util.h instrument.h mix.h
+ $(CC) $(CPPFLAGS) $< -o $@
+
+main: $(SOURCES:.c=.o)
+ $(CC) $(LIBS) -o $@ $+
+
+.PHONY: clean all play clear cat tur coffin
+
+clean:
+ @echo "Cleaning..."
+ -@rm -rf *.o main TAGS *.dYSM 2>/dev/null || true
+ @echo "Done."
+
+$(out): main
+ ./main
+
+# play: $(out)
+# ffplay -f f32le -ar 44100 -showmode 1 -autoexit output.mkv
+
+play: $(out)
+ mpv $<
+
+tur: main
+ @echo "making turkish march..."
+ -@rm notes.txt
+ @ln -s turkish/turkish\ notes.txt notes.txt
+ @./main
+ @echo "Done."
+
+marble: main
+ @echo "making marble machine..."
+ -@rm notes.txt
+ @ln -s marble\ machine/marble\ machine\ notes.txt notes.txt
+ @./main
+ @echo "Done."
+
+cat: main
+ @echo "making cat vibing..."
+ -@rm notes.txt
+ @ln -s cat/part\ of\ cat\ vibing.txt notes.txt
+ @./main
+ @echo "Done."
+
+coffin: main
+ @echo "making coffin dance..."
+ -@rm notes.txt
+ @ln -s coffin/part\ of\ coffin\ dance.txt notes.txt
+ @./main
+ @echo "Done."
+
+elise: main
+ @echo "making Für Elise..."
+ -@rm notes.txt
+ @ln -s "Für Elise/Für Elise notes.txt" notes.txt
+ @./main
+ @echo "Done."
+
+single: main
+ @echo "making Single Notes..."
+ -@rm notes.txt
+ @ln -s "test.txt" notes.txt
+ @./main
+ @echo "Done."
+
+
+clear:
+ @echo "Deleting $(out)..."
+ -@rm $(out)
+ @echo "Done."
+
+test-parser.o: parser.c parser.h util.h
+ $(CC) -Wall -c -DTEST -o $@ $<
+
+test: util.o test-parser.o instrument.o
+ $(CC) -Wall -O2 -o $@ $+
diff --git a/marble machine/marble machine notes.txt b/marble machine/marble machine notes.txt
new file mode 100644
index 0000000..a56415f
--- /dev/null
+++ b/marble machine/marble machine notes.txt
@@ -0,0 +1,5 @@
+,
+7 1.5 , -17
+2 0.5 , (-14 -10 1)
+2 1.5 , s
+0 0.5 , (-14 -10 1)
diff --git a/mix.c b/mix.c
new file mode 100644
index 0000000..ae1c1ab
--- /dev/null
+++ b/mix.c
@@ -0,0 +1,22 @@
+#include "mix.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Mix different sounds together. For now this is just a simple
+ averaging function. But more sophostigated mixing techniques such
+ as filters and reverbs might get includede in the future. */
+
+U_ATTR
+Pulse
+mix_sound(Wave w, LENT n)
+{
+ Pulse result = 0.0f;;
+
+ for (LENT i = 0; i < n;)
+ result += *(w+i++);
+
+ result /= (float) n;
+
+ return result;
+}
+
diff --git a/mix.d b/mix.d
new file mode 100644
index 0000000..7f9f2f4
--- /dev/null
+++ b/mix.d
@@ -0,0 +1 @@
+mix.o mix.d : mix.c mix.h util.h
diff --git a/mix.h b/mix.h
new file mode 100644
index 0000000..9f11f95
--- /dev/null
+++ b/mix.h
@@ -0,0 +1,7 @@
+#ifndef MIX_H
+#define MIX_H
+#include "util.h"
+
+U_ATTR Pulse mix_sound(Wave w, LENT n);
+
+#endif
diff --git a/notes.txt b/notes.txt
new file mode 120000
index 0000000..0c7a840
--- /dev/null
+++ b/notes.txt
@@ -0,0 +1 @@
+Für Elise/Für Elise notes.txt \ No newline at end of file
diff --git a/parser.c b/parser.c
new file mode 100644
index 0000000..8d34121
--- /dev/null
+++ b/parser.c
@@ -0,0 +1,694 @@
+#include "parser.h"
+#include "mix.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+/* NONE_TYPE is for empty notes. */
+/* Since different channels have different time specifications, an
+ empty line can only be parsed as an empty note. So we have to
+ explicitly mark a silent note. */
+
+/* A note of type BEAT_DURATION_TYPE stores the new bpm in the secs
+ field. */
+
+typedef enum {
+ NONE_TYPE,
+ NOTE_TYPE,
+ SILENT_TYPE,
+ INSTRUMENT_TYPE,
+ BEAT_DURATION_TYPE
+} PType;
+
+struct PNote_s {
+ Semitones *tones;
+ Beats secs;
+ LENT len;
+ PType type;
+
+ /* The instrument type, if it is an instrument changing note. */
+ InstrumentTypes *name;
+};
+
+struct PUnit_s {
+ PNote *notes; /* the notes of channels */
+ LENT len; /* the length of channels */
+};
+
+/* Helper macro */
+
+#define EMPTY_NOTE ((PNote) { NULL, 0.0f, 0, NONE_TYPE, NULL })
+#define EMPTY_UNIT ((PUnit) { NULL, 0 })
+
+/* Helper function */
+
+H_ATTR
+unsigned char
+is_empty_note(PNote *n)
+{
+ return n->type == NONE_TYPE;
+}
+
+/* Destructor */
+
+U_ATTR
+void
+destroy_sheet(PSheet sh)
+{
+ for (LENT i = 0; i < sh.len; i++) {
+ for (LENT j = 0; j < (sh.data+i)->len; j++) {
+ if (((sh.data+i)->notes+j)->tones)
+ free(((sh.data+i)->notes+j)->tones);
+ if (((sh.data+i)->notes+j)->name)
+ free(((sh.data+i)->notes+j)->name);
+ }
+
+ free((sh.data+i)->notes);
+ }
+
+ free(sh.data);
+}
+
+/* For debugging */
+
+U_ATTR
+static void
+print_note(const PNote note)
+{
+ switch (note.type) {
+ case NONE_TYPE:
+ fprintf(stderr, "an empty note\n");
+ break;
+ case SILENT_TYPE:
+ fprintf(stderr, "a silent note with %.3f beats\n",
+ note.secs);
+ break;
+ case NOTE_TYPE:
+ fprintf(stderr, "a note with %.3f beats and %lu semitone%s\n",
+ note.secs, note.len, (note.len < 2) ? "" : "s");
+ fprintf(stderr, "The semitone%s: ",
+ (note.len<2) ? " is" : "s are");
+ for (LENT i = 0; i < note.len; i++)
+ fprintf(stderr, "%.2f%s",
+ *(note.tones+i),
+ (i==note.len-1) ? "" : ", ");
+ fprintf(stderr, "\n");
+ break;
+ case INSTRUMENT_TYPE:
+ fprintf(stderr, "an instrument changing note\n");
+ break;
+ case BEAT_DURATION_TYPE:
+ fprintf(stderr, "a note of the type that changes the bpm to "
+ "%.3f beats per minute.\n",
+ note.secs);
+ break;
+ }
+}
+
+U_ATTR
+static void
+print_unit(const PUnit u)
+{
+ fprintf(stderr, "Printing a unit with %lu channel%s\n",
+ u.len, (u.len>1) ? "s" : "");
+ for (LENT i = 0; i < u.len; i++) {
+#ifdef DEBUG
+ fprintf(stderr, "The %lu-th note: ", i);
+ fprintf(stderr, "%p\n", (u.notes+i));
+#endif
+ print_note(*(u.notes+i));
+ fprintf(stderr, "\n");
+ }
+}
+
+U_ATTR
+void
+print_sheet(const PSheet sh)
+{
+ for (LENT i = 0; i < sh.len;) print_unit(*(sh.data+i++));
+}
+
+/* The main read function.
+
+ Warning: A valid sheet file should have a "channel specification"
+ in the first line. A "channel specification" line is a line that
+ has no numbers, but just semi-colons. If there are N channels,
+ then there should be N-1 semi-colons in the specification line.
+ What if there are no channels? Why read a sheet without
+ channels?
+
+ If there are no specification lines in the beginning, then one
+ channel format will be assumed. But this is prone to errors, and
+ not recommended. */
+
+U_ATTR
+PSheet
+read_sheet(const char *str, LENT len)
+{
+ PSheet result = { NULL, 0 };
+
+ Node_t stack = EMPTY_NODE, stack_next = EMPTY_NODE;
+ Node_t *stack_pointer = NULL;
+
+ LENT counter = 0, increment = 0, line_count = 0;
+
+ LENT stack_len = 0;
+
+ char current = 0;
+
+ float local = 0.0f;
+
+ int channel_num = 0, num_chars_read = 0;
+ int num_channels = 0;
+
+ unsigned char first_line = 1, note_used = 0;
+
+ PUnit *unit_pointer = NULL;
+ PUnit current_unit = EMPTY_UNIT;
+
+ PUnit *unit_stack = NULL;
+ int unit_stack_len = 0;
+
+ /* A note cannot consist of more than 5 semitones. One more
+ semitone will be allocated so that it can store the time
+ information. */
+ Semitones *st_stack = NULL;
+ int st_stack_len = 0;
+
+ InstrumentTypes *ins_pointer = NULL;
+
+ PNote temp_note = EMPTY_NOTE, *notes_pointer = NULL;
+
+ Semitones *st_pointer = NULL;
+
+ /* A note without time information will use the previous beats
+ information. A first note without time information will use 1.0f
+ as the beats. */
+
+ Beats *beats_channel_array = NULL;
+
+ for (;counter < len;) {
+ current = *(str+counter);
+
+ increment = 1;
+
+ switch (current) {
+ /* 13 is also a newline character, it seems. */
+ case 13:
+ case '\n':
+ if (!note_used)
+ free(current_unit.notes);
+
+ if (first_line) {
+ num_channels = channel_num+1;
+ current_unit.len = num_channels;
+ beats_channel_array = MYALLOC(Beats, num_channels);
+
+ for (int i = 0; i < num_channels; i++) {
+ *(beats_channel_array+i) = 1.0f;
+ }
+
+ } else if (note_used) {
+ unit_pointer = MYALLOC(PUnit, 1);
+ *unit_pointer = current_unit;
+ PUSH_NODE(stack_len, stack_pointer, stack,
+ unit_pointer, v);
+ /* Since it is used, we set its pointer to NULL to prevent
+ accidental frees. */
+ current_unit.notes = NULL;
+ }
+
+ notes_pointer = MYALLOC(PNote, num_channels);
+
+ for (int i = 0; i < num_channels;)
+ *(notes_pointer+i++) = EMPTY_NOTE;
+
+ current_unit.notes = notes_pointer;
+
+ channel_num = 0;
+ first_line = 0;
+ line_count++;
+ note_used = 0;
+ break;
+ case '#':
+ /* Comments */
+ for (;*(str+counter) != '\n' &&
+ *(str+counter) != 13;) counter++;
+ increment = 2;
+ counter -= 2;
+ break;
+ case ',':
+ note_used = 1;
+ channel_num++;
+ break;
+ default:
+ /* Limited support for a sheet without channel specifications. */
+
+ if (first_line) {
+ num_channels = (channel_num=0)+1;
+ current_unit.len = num_channels;
+
+ notes_pointer = MYALLOC(PNote, num_channels);
+ beats_channel_array = MYALLOC(Beats, num_channels);
+
+ for (int i = 0; i < num_channels; i++) {
+ *(notes_pointer+i) = EMPTY_NOTE;
+ *(beats_channel_array+i) = 1.0f;
+ }
+
+ current_unit.notes = notes_pointer;
+
+ first_line = 0;
+ }
+
+ note_used = 1;
+ /* fprintf(stderr, "here the char is %c\n", *(str+counter)); */
+ if (*(str+counter) == ' ') {
+ /* Just skip spaces so that they don't block us reading anchor
+ chars. */
+ } else if (*(str+counter) == 'b') {
+ /* changing bpm note */
+
+ counter++;
+
+ for (;*(str+counter) == ' ';) counter++;
+
+ if (*(str+counter) != '\n' &&
+ *(str+counter) != 13 &&
+ sscanf(str+counter, "%f%n",
+ &local, &num_chars_read) == 1) {
+ temp_note = (PNote) {
+ NULL, local,
+ 0, BEAT_DURATION_TYPE, NULL
+ };
+
+ /* fprintf(stderr, "print a note\n");
+ * print_note(temp_note); */
+
+ counter += num_chars_read;
+ }
+ *(current_unit.notes+channel_num) = temp_note;
+ } else if (*(str+counter) == 's') {
+ /* a silent note */
+ counter++;
+
+ /* skip all spaces that follow */
+ for (;*(str+counter) == ' ';) counter++;
+
+ if (*(str+counter) != '\n' &&
+ *(str+counter) != 13 &&
+ sscanf(str+counter, "%f%n",
+ &local, &num_chars_read) == 1) {
+ *(beats_channel_array+channel_num) = local;
+ temp_note = (PNote) {
+ NULL, local,
+ 1, SILENT_TYPE, NULL
+ };
+
+ increment = num_chars_read+1;
+ } else {
+ /* a note without time; use the previous beat instead. */
+ temp_note = (PNote) {
+ NULL, *(beats_channel_array+channel_num),
+ 1, SILENT_TYPE, NULL
+ };
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "print a note while reading\n");
+ print_note(temp_note);
+ fprintf(stderr, "\n\n\n\n");
+#endif
+
+ *(current_unit.notes+channel_num) = temp_note;
+ temp_note = EMPTY_NOTE;
+ /* subtract one from the increment so that we don't
+ unknowingly skip one character. */
+ increment--;
+ } else if (sscanf(str+counter, "%f%n",
+ &local, &num_chars_read) == 1) {
+ /* a note consisting of one single semitone */
+ st_pointer = MYALLOC(Semitones, 1);
+ *st_pointer = local;
+ counter += num_chars_read;
+
+ /* skip all spaces that follow */
+ for (;*(str+counter) == ' ';) counter++;
+
+
+ if (*(str+counter) != '\n' &&
+ *(str+counter) != 13 &&
+ sscanf(str+counter, "%f%n",
+ &local, &num_chars_read) == 1) {
+ *(beats_channel_array+channel_num) = local;
+ temp_note = (PNote) {
+ st_pointer, local,
+ 1, NOTE_TYPE, NULL
+ };
+
+ increment = num_chars_read+1;
+ } else {
+ /* a note without time; use the previous beat instead. */
+ temp_note = (PNote) {
+ st_pointer, *(beats_channel_array+channel_num),
+ 1, NOTE_TYPE, NULL
+ };
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "print a note while reading\n");
+ print_note(temp_note);
+ fprintf(stderr, "\n\n\n\n");
+#endif
+
+ *(current_unit.notes+channel_num) = temp_note;
+ temp_note = EMPTY_NOTE;
+ /* subtract one from the increment so that we don't
+ unknowingly skip one character. */
+ increment--;
+ } else if (*(str+counter) == '(') {
+ /* a note consisting of multiple semitones */
+ st_stack = MYALLOC(Semitones, 10);
+ st_stack_len = 0;
+ counter++;
+
+ for (;sscanf(str+counter, "%f %n", &local, &num_chars_read) == 1;
+ counter += num_chars_read) {
+ *(st_stack+st_stack_len++) = local;
+ }
+
+ /* Now skip to the closing parenthesis */
+ for (;counter < len && *(str+counter) != ')';) counter++;
+
+ if (st_stack_len>1) {
+ st_pointer = MYALLOC(Semitones, st_stack_len-1);
+
+ for (int i = 0; i < st_stack_len-1; i++)
+ *(st_pointer+i) = *(st_stack+i);
+
+ *(beats_channel_array+channel_num) =
+ *(st_stack+st_stack_len-1);
+
+ temp_note = (PNote) {
+ st_pointer, *(st_stack+st_stack_len-1),
+ st_stack_len-1, NOTE_TYPE, NULL
+ };
+ } else if (!st_stack_len) {
+ /* empty note */
+ temp_note = EMPTY_NOTE;
+ } else {
+ /* one semitone note */
+ st_pointer = MYALLOC(Semitones, 1);
+ *st_pointer = *st_stack;
+
+ temp_note = (PNote) {
+ st_pointer, *(beats_channel_array+channel_num),
+ 1, NOTE_TYPE, NULL
+ };
+ }
+
+ free(st_stack);
+
+ *(current_unit.notes+channel_num) = temp_note;
+ temp_note = EMPTY_NOTE;
+ } else if (*(str+counter) == 'i') {
+ /* an instrument changing unit */
+ /* Just piano for now */
+ ins_pointer = MYALLOC(InstrumentTypes, 1);
+ *ins_pointer = PIANO;
+ *(current_unit.notes+channel_num) = (PNote) {
+ NULL, 0, 0, INSTRUMENT_TYPE, ins_pointer
+ };
+ } else {
+ /* invalid input */
+ fprintf(stderr, "Invalid input in the sheet.\n"
+ "channel = %d, line = %lu\n",
+ channel_num, line_count);
+ }
+
+ break;
+ }
+
+ counter += increment;
+ }
+
+ free(current_unit.notes);
+ free(beats_channel_array);
+
+ /* Then fill the variables up with the data we have read. */
+
+ LENT total_len = stack_len;
+
+ unit_stack = MYALLOC(PUnit, total_len);
+
+ for (LENT i = 0; i < total_len; i++) {
+ POP_NODE(stack_len, unit_pointer, stack, stack_next, v);
+ *(unit_stack+total_len-1-(unit_stack_len++)) =
+ *unit_pointer;
+ free(unit_pointer);
+ }
+
+ result.data = unit_stack;
+ result.len = total_len;
+
+ return result;
+}
+
+/* Get the number of note_type notes in a sheet.
+
+ Also calculate the number of notes per channel and store in
+ nums_per_channel, which is assumed to be already allocated. */
+
+U_ATTR
+static LENT
+sheet_note_num(const PSheet sh, LENT *nums_per_channel)
+{
+ LENT note_num = 0;
+
+ for (LENT i = 0; i < (sh.data)->len;)
+ *(nums_per_channel+i++) = 0;
+
+ for (LENT i = 0; i < sh.len; i++) {
+ PUnit pu = *(sh.data+i);
+
+ for (LENT j = 0; j < pu.len; j++)
+ if ((pu.notes+j)->type == NOTE_TYPE ||
+ (pu.notes+j)->type == SILENT_TYPE) {
+ note_num++;
+ *(nums_per_channel+j) += 1;
+ }
+ }
+
+ return note_num;
+}
+
+H_ATTR
+static WaveFrag
+silent_note(Beats b, float dur)
+{
+ WaveFrag w;
+
+ LENT sample_num = (LENT) floor(SAMPLE_RATE * b * dur);
+
+ w.w = MYALLOC(Pulse, sample_num);
+ w.n = sample_num;
+
+ for (LENT i = 0; i < sample_num; i++)
+ *(w.w+i) = 0.0f;
+
+ return w;
+}
+
+U_ATTR
+WaveFrag
+play_sheet(const PSheet sh, const Volume v)
+{
+ WaveFrag wf = (WaveFrag) { NULL, 0 };
+
+ /* If there is not unit or if the channel number is zero then we
+ return an empty wave fragment. */
+
+ if (!(sh.len) || !(sh.data->len)) return wf;
+
+ Instrument *piano = make_piano();
+ Instrument *violin = make_violin();
+ Instrument *inst = piano;
+
+ /* For mixing sounds in notes */
+ WaveFrag *war = NULL;
+ WaveFrag **temp = NULL;
+ LENT *channel_num_array = NULL;
+ /* For mixing semitones */
+ WaveFrag *st_temp = MYALLOC(WaveFrag, 10);
+
+ LENT total_len = 0, channel_num = sh.data->len;
+
+ LENT *num_notes_per_channel = MYALLOC(LENT, channel_num);
+
+ Beats dur = BEAT_DUR;
+
+#ifdef DEBUG
+ fprintf(stderr, "before finding num\n");
+#endif
+
+ total_len = sheet_note_num(sh, num_notes_per_channel);
+
+ /* for (LENT i = 0; i < sh.len;)
+ * total_len += (sh.data+i++)->len; */
+
+ /* Allocate spaces */
+
+ temp = MYALLOC(WaveFrag*, channel_num);
+ channel_num_array = MYALLOC(LENT, channel_num);
+
+ for (LENT i = 0; i < channel_num; i++) {
+
+#ifdef DEBUG
+ fprintf(stderr, "num_notes_per_channel + %lu = %lu\n",
+ i, *(num_notes_per_channel+i));
+#endif
+
+ *(temp+i) = MYALLOC(WaveFrag, *(num_notes_per_channel+i));
+ }
+
+ war = MYALLOC(WaveFrag, channel_num);
+
+ for (LENT i = 0; i < channel_num;) *(channel_num_array+i++) = 0;
+
+#ifdef DEBUG
+ fprintf(stderr, "before for loop\n");
+#endif
+
+ for (LENT i = 0; i < sh.len; i++) {
+ PUnit pu = *(sh.data+i);
+
+ if (pu.len != channel_num) {
+ fprintf(stderr, "Every unit should have the same number of channels.\n"
+ "But unit %lu has %lu channels\n",
+ i, pu.len);
+ exit(1);
+ }
+
+ for (LENT j = 0; j < channel_num; j++) {
+ PNote pn = *(pu.notes+j);
+
+ if (pn.type != NOTE_TYPE &&
+ pn.type != SILENT_TYPE &&
+ pn.type != BEAT_DURATION_TYPE) continue;
+
+ if (pn.len >= 10) {
+ fprintf(stderr, "A note cannot have more than 10 semitones together.\n");
+ fprintf(stderr, "node %lu and number = %lu\n", j, pn.len);
+ exit(1);
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "i = %lu, j = %lu\n", i, j);
+#endif
+
+ switch (pn.type) {
+ case NOTE_TYPE:
+ for (LENT k = 0; k < pn.len; k++) {
+ *(st_temp+k) = make_sound(inst, v * ((k==1) ? 0.3f : 1.0f),
+ stoh(*(pn.tones+k)), pn.secs * dur);
+ }
+ break;
+ case BEAT_DURATION_TYPE:
+ dur = 60.0f / pn.secs;
+ break;
+ default:
+ /* SILENT_TYPE */
+ *(st_temp) = silent_note(pn.secs, dur);
+ break;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "after making sound\n");
+ if (j==1) {
+ print_note(pn);
+ fprintf(stderr, "channel num + 1 = %lu\n", *(channel_num_array+1));
+ }
+#endif
+
+ *(*(temp+j)+*(channel_num_array+j)) =
+ mix_waves(st_temp, pn.len);
+
+#ifdef DEBUG
+ fprintf(stderr, "after mixing\n");
+#endif
+
+ *(channel_num_array+j) = *(channel_num_array+j) + 1;
+ /* *(temp + i*channel_num + j) = mix_waves(st_temp, pn.len); */
+
+ for (int k = 0; k < pn.len; k++)
+ free((st_temp+k)->w);
+ }
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "after for loop\n");
+#endif
+
+ free(st_temp);
+
+ /* Now all the waves are produced. We just need to merge and mix
+ them. */
+
+ /* Note: merge_waves free the waves in the input. */
+
+ for (LENT j = 0; j < channel_num; j++)
+ *(war+j) = merge_waves(inst, *(temp+j),
+ *(num_notes_per_channel+j),
+ 1);
+ free(temp);
+ free(num_notes_per_channel);
+ free(channel_num_array);
+
+#ifdef DEBUG
+ fprintf(stderr, "after merging\n");
+#endif
+
+ wf = mix_waves(war, channel_num);
+
+ for (LENT i = 0; i < channel_num;) free((war+i++)->w);
+
+ free(war);
+ destroy_piano(piano);
+ destroy_violin(violin);
+
+#ifdef DEBUG
+ fprintf(stderr, "after mixing\n");
+#endif
+
+ return wf;
+}
+
+/* An embedded test function */
+
+#ifdef TEST
+int
+main(int argc, char **argv)
+{
+ char *file_content = MYALLOC(char, 512);
+
+ LENT len = read_entire_file("test.txt", &file_content);
+
+ PSheet sh = read_sheet(file_content, len);
+
+ print_sheet(sh);
+
+ WaveFrag wf = play_sheet(sh, 0.3f);
+
+ destroy_sheet(sh);
+
+ printf("w.n = %lu\n", wf.n);
+ printf("some sound is %f\n", *(wf.w+10));
+
+ free(wf.w);
+
+ free(file_content);
+
+ return 0;
+}
+#endif
diff --git a/parser.d b/parser.d
new file mode 100644
index 0000000..0d896e2
--- /dev/null
+++ b/parser.d
@@ -0,0 +1 @@
+parser.o parser.d : parser.c parser.h util.h instrument.h mix.h
diff --git a/parser.h b/parser.h
new file mode 100644
index 0000000..3454bd4
--- /dev/null
+++ b/parser.h
@@ -0,0 +1,48 @@
+#ifndef PARSER_H
+#define PARSER_H
+#include "util.h"
+#include "instrument.h"
+
+/* Parse a complex sheet file so that it is easy to generate complex
+ sounds. */
+
+/* A unit for the parser. This typically represents one note, but can
+ serve different purposes. In our format, a unit is given in one
+ line. */
+
+struct PUnit_s;
+
+typedef struct PUnit_s PUnit;
+
+struct PNote_s;
+
+typedef struct PNote_s PNote;
+
+/* A sheet is just an array of units. But we need to know the length
+ of the array as well. */
+
+typedef struct {
+ PUnit *data;
+ LENT len;
+} PSheet;
+
+U_ATTR void print_sheet(const PSheet sh);
+
+/* Might be helpful, or not */
+
+H_ATTR unsigned char is_empty_note(PNote *n);
+
+/* The function to read the sheet. */
+
+U_ATTR PSheet read_sheet(const char *str, LENT len);
+
+/* Destructor */
+
+U_ATTR void destroy_sheet(PSheet sh);
+
+/* Now play sheet */
+
+U_ATTR WaveFrag play_sheet(const PSheet sh, const Volume v);
+
+#endif
+
diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..cd1b6af
--- /dev/null
+++ b/test.txt
@@ -0,0 +1,3 @@
+3 2
+4
+5
diff --git a/turkish/turkish notes (archive).txt b/turkish/turkish notes (archive).txt
new file mode 100644
index 0000000..539482e
--- /dev/null
+++ b/turkish/turkish notes (archive).txt
@@ -0,0 +1,236 @@
+2 0.25
+0 0.25
+-1 0.25
+0 0.25
+3 0.5
+-100 0.5
+5 0.25
+3 0.25
+2 0.25
+3 0.25
+7 0.5
+-100 0.5
+8 0.25
+7 0.25
+6 0.25
+7 0.25
+14 0.25
+12 0.25
+11 0.25
+12 0.25
+14 0.25
+12 0.25
+10 0.25
+12 0.25
+15 1.0
+12 0.5
+15 0.5
+14 0.5
+12 0.5
+10 0.5
+12 0.5
+14 0.5
+12 0.5
+10 0.5
+12 0.5
+14 0.5
+12 0.5
+10 0.5
+12 0.5
+7 1
+2 0.25
+0 0.25
+-1 0.25
+0 0.25
+3 0.5
+-100 0.5
+5 0.25
+3 0.25
+2 0.25
+3 0.25
+7 0.5
+-100 0.5
+8 0.25
+7 0.25
+6 0.25
+7 0.25
+14 0.25
+12 0.25
+11 0.25
+12 0.25
+14 0.25
+12 0.25
+10 0.25
+12 0.25
+15 1.0
+12 0.5
+15 0.5
+14 0.5
+12 0.5
+10 0.5
+12 0.5
+14 0.5
+12 0.5
+10 0.5
+12 0.5
+14 0.5
+12 0.5
+10 0.5
+12 0.5
+7 1
+7 0.5
+8 0.5
+10 0.5
+10 0.5
+12 0.25
+10 0.25
+8 0.25
+7 0.25
+5 0.5
+5 0.5
+7 0.5
+8 0.5
+10 0.5
+10 0.5
+12 0.25
+10 0.25
+8 0.25
+7 0.25
+5 1
+3 0.5
+5 0.5
+7 0.5
+7 0.5
+8 0.25
+7 0.25
+5 0.25
+3 0.25
+2 0.5
+2 0.5
+3 0.5
+5 0.5
+7 0.5
+7 0.5
+8 0.25
+7 0.25
+5 0.25
+3 0.25
+2 1
+2 0.25
+0 0.25
+-2 0.25
+0 0.25
+3 0.5
+-100 0.5
+5 0.25
+3 0.25
+2 0.25
+3 0.25
+7 0.5
+-100 0.5
+8 0.25
+7 0.25
+6 0.25
+7 0.25
+14 0.25
+12 0.25
+11 0.25
+12 0.25
+14 0.25
+12 0.25
+10 0.25
+12 0.25
+15 1
+12 0.5
+14 0.5
+15 0.5
+14 0.5
+12 0.5
+11 0.5
+12 0.5
+7 0.5
+8 0.5
+5 0.5
+3 1
+2 0.75
+0 0.125
+2 0.125
+0 1
+7 0.5
+8 0.5
+10 0.5
+10 0.5
+12 0.25
+10 0.25
+8 0.25
+7 0.25
+5 0.5
+5 0.5
+7 0.5
+8 0.5
+10 0.5
+10 0.5
+12 0.25
+10 0.25
+8 0.25
+7 0.25
+5 1
+3 0.5
+5 0.5
+7 0.5
+7 0.5
+8 0.25
+7 0.25
+5 0.25
+3 0.25
+2 0.5
+2 0.5
+3 0.5
+5 0.5
+7 0.5
+7 0.5
+8 0.25
+7 0.25
+5 0.25
+3 0.25
+2 1
+2 0.25
+0 0.25
+-2 0.25
+0 0.25
+3 0.5
+-100 0.5
+5 0.25
+3 0.25
+2 0.25
+3 0.25
+7 0.5
+-100 0.5
+8 0.25
+7 0.25
+6 0.25
+7 0.25
+14 0.25
+12 0.25
+11 0.25
+12 0.25
+14 0.25
+12 0.25
+10 0.25
+12 0.25
+15 1
+12 0.5
+14 0.5
+15 0.5
+14 0.5
+12 0.5
+11 0.5
+12 0.5
+7 0.5
+8 0.5
+5 0.5
+3 1
+2 0.75
+0 0.125
+2 0.125
+0 1
diff --git a/turkish/turkish notes.txt b/turkish/turkish notes.txt
new file mode 100644
index 0000000..548ec48
--- /dev/null
+++ b/turkish/turkish notes.txt
@@ -0,0 +1,127 @@
+,
+2 0.25 , s
+0 0.25 , -12 0.5
+-1 0.25 , (-9 -5 0.5)
+0 0.25 , (-9 -5 0.5)
+3 0.5 , (-9 -5 0.5)
+-100 0.5 , -12 0.5
+5 0.25 , (-9 -5 0.5)
+3 0.25 , (-9 -5 0.5)
+2 0.25 , (-9 -5 0.5)
+3 0.25 , -12 0.5
+7 0.5 , (-9 -5 0.5)
+-100 0.5 , -12 0.5
+8 0.25 , (-9 -5 0.5)
+7 0.25 , -12 0.5
+6 0.25 , (-9 -5 0.5)
+7 0.25 , (-9 -5 0.5)
+14 0.25 , (-9 -5 0.5)
+12 0.25 , -17 0.5
+11 0.25 , (-5 -10 0.5)
+12 0.25 , (-5 -10 0.5)
+14 0.25 , (-5 -10 0.5)
+12 0.25 , -17 0.5
+10 0.25 , (-5 -10 0.5)
+12 0.25 , (-5 -10 0.5)
+15 , (-5 -10 0.5)
+12 0.5 , -17 0.5
+15 0.5 , (-5 -10 0.5)
+10 0.01 , -10 0.5
+12 0.01 , -10 0.5
+14 0.48 , -17
+(12 9 0.5) ,
+(7 10 0.5) ,
+(8 12 0.5) ,
+10 0.01 ,
+12 0.01 ,
+14 0.48 ,
+(12 9 0.5) ,
+(7 10 0.5) ,
+(8 12 0.5) ,
+10 0.01 ,
+12 0.01 ,
+14 0.48 ,
+(12 9 0.5) ,
+(7 10 0.5) ,
+(8 12 0.5) ,
+7 ,
+2 0.25 , s
+0 0.25 , -12 0.5
+-1 0.25 , (-9 -5 0.5)
+0 0.25 , (-9 -5 0.5)
+3 0.5 , (-9 -5 0.5)
+-100 0.5 , -12 0.5
+5 0.25 , (-9 -5 0.5)
+3 0.25 , (-9 -5 0.5)
+2 0.25 , (-9 -5 0.5)
+3 0.25 , -12 0.5
+7 0.5 , (-9 -5 0.5)
+-100 0.5 , -12 0.5
+8 0.25 , (-9 -5 0.5)
+7 0.25 , -12 0.5
+6 0.25 , (-9 -5 0.5)
+7 0.25 , (-9 -5 0.5)
+14 0.25 , (-9 -5 0.5)
+12 0.25 , -17 0.5
+11 0.25 , (-5 -10 0.5)
+12 0.25 , (-5 -10 0.5)
+14 0.25 , (-5 -10 0.5)
+12 0.25 , -17 0.5
+10 0.25 , (-5 -10 0.5)
+12 0.25 , (-5 -10 0.5)
+15 , (-5 -10 0.5)
+12 0.5 , -17 0.5
+15 0.5 , (-5 -10 0.5)
+10 0.01 , -10 0.5
+12 0.01 , -10 0.5
+14 0.48 , -17
+(12 9 0.5) ,
+(7 10 0.5) ,
+(8 12 0.5) ,
+10 0.01 ,
+12 0.01 ,
+14 0.48 ,
+(12 9 0.5) ,
+(7 10 0.5) ,
+(8 12 0.5) ,
+10 0.01 ,
+12 0.01 ,
+14 0.48 ,
+(12 9 0.5) ,
+(7 10 0.5) ,
+(8 12 0.5) ,
+7 ,
+(3 7 0.5) , s
+(5 8 0.5) , -21 0.5
+(7 10 0.5) , -9 0.5
+(7 10 0.5) , -17 0.5
+12 0.25 , -5 0.5
+10 0.25 , -14
+8 0.25 , s
+7 0.25 ,
+(2 5 0.5) ,
+(-2 5 0.5) ,
+(3 7 0.5) ,
+(5 8 0.5) ,
+# third line over
+(7 10 0.5) , -21 0.5
+(7 10 0.5) , -9 0.5
+12 0.25 , -17 0.5
+10 0.25 , -5 0.5
+8 0.25 , -14
+7 0.25 , s
+(2 5 0.5) , -24 0.5
+s 0.5 , -12 0.5
+(0 3 0.5) , -21 0.5
+(2 5 0.5) , -9 0.5
+(3 7 0.5) , -17
+(3 7 0.5) , s
+8 0.25 ,
+7 0.25 ,
+5 0.25 ,
+3 0.25 ,
+(-1 2 0.5) ,
+(-5 2 0.5) ,
+(0 3 0.5) ,
+(2 5 0.5) ,
+# fourth line over
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..c5921e5
--- /dev/null
+++ b/util.c
@@ -0,0 +1,80 @@
+#include "util.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+UNUSED
+long
+read_entire_file (const char *file_name, char **str)
+{
+ long file_size = 0;
+
+ FILE *file = fopen(file_name, "r");
+
+ if (!file) {
+ fprintf(stderr, "Cannot open file \"%s\": ", file_name);
+ perror(NULL);
+ return 0;
+ }
+
+ fseek(file, 0, SEEK_END);
+ file_size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ *str = (char*)realloc(*str, file_size+1);
+ *((*str)+file_size) = 0;
+
+ fread(*str, sizeof(char), file_size, file);
+
+ fclose(file);
+ return file_size;
+}
+
+UC_ATTR
+LENT
+max(const LENT a, const LENT b)
+{
+ return (a < b) ? b : a;
+}
+
+U_ATTR
+LENT
+min(LENT a, LENT b)
+{
+ return (a < b) ? a : b;
+}
+
+U_ATTR
+WaveFrag
+mix_waves(WaveFrag *frags, LENT len)
+{
+ WaveFrag wf = (WaveFrag) { NULL, 0 };
+
+ LENT max_len = 0;
+
+ Pulse p = 0.0f;
+
+ for (LENT i = 0; i < len; i++)
+ max_len = max(max_len, (frags+i)->n);
+
+ wf.n = max_len;
+ wf.w = MYALLOC(Pulse, max_len);
+
+ for (LENT i = 0; i < max_len; i++) {
+ p = 0.0f;
+
+ for (LENT j = 0; j < len; j++)
+ if (i < (frags+j)->n) p += *((frags+j)->w+i);
+
+ *(wf.w+i) = p / (float) len;
+ }
+
+ return wf;
+}
+
+HC_ATTR
+Hertz
+stoh(const Semitones st)
+{
+ return STDP * pow(STD_BASE, st);
+}
diff --git a/util.d b/util.d
new file mode 100644
index 0000000..329aa4d
--- /dev/null
+++ b/util.d
@@ -0,0 +1 @@
+util.o util.d : util.c util.h
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..700a8bf
--- /dev/null
+++ b/util.h
@@ -0,0 +1,126 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+/* Mostly types, constants, and macros */
+
+/* A simple singly linked list */
+
+/* Push a node to the singly linked list. */
+#define PUSH_NODE(NUM_VAR, POINTER, CUR, VALUE, TYPE) { \
+ (NUM_VAR)++; \
+ if (NUM_VAR > 1) { \
+ (POINTER) = MYALLOC(Node_t, 1); \
+ *(POINTER) = (CUR); \
+ (CUR) = (Node_t) { (Node_v) { .TYPE=(VALUE) }, (POINTER) }; \
+ } else { \
+ (CUR) = (Node_t) { (Node_v) { .TYPE=(VALUE) }, NULL }; \
+ } \
+ }
+
+/* Pop one node out of the singly linked list. */
+#define POP_NODE(NUM_VAR, X, CUR, NEXT, TYPE) { \
+ (NUM_VAR)--; \
+ X = CUR.value.TYPE; \
+ if (CUR.next) { \
+ NEXT = *(CUR.next); \
+ free(CUR.next); \
+ CUR = NEXT; \
+ } \
+ }
+
+/* An empty node. */
+#define EMPTY_NODE (Node_t) { (Node_v) { .f=0 }, NULL }
+
+typedef union {
+ float f;
+ void *v; /* very evil */
+} Node_v;
+
+typedef struct Node {
+ Node_v value;
+ struct Node *next;
+} Node_t;
+
+/* macros */
+
+#define MYALLOC(X, Y) ((X *) malloc(sizeof(X)*Y)) /* The most useful macro perhaps */
+
+/* Constant macros */
+
+#define SAMPLE_RATE 44100.0f
+#define DEFAULT_OUTPUT_NAME "output.mp3"
+#define STDP 440.0f
+/* I don't want to use a global variable for this constant. */
+#define STD_BASE 1.059463094359295f
+/* TODO: Set Volume, and other settings in the sheet. */
+/* These can now be changed in the sheet. */
+#define BPM 120.0f
+#define BEAT_DUR (60.0f / BPM)
+
+/* Frequency modulation */
+
+#define FM_FREQUENCY 2.0f
+#define FM_AMP 0.02f
+
+/* For ADSR */
+#define ATTACK_P 0.01f
+#define DECAY_P 0.495f
+#define RELEASE_P 0.49f
+#define SUSTAIN_P (1.0f-(ATTACK_P+DECAY_P+RELEASE_P))
+#define SUSTAIN_LEVEL 0.7f
+
+/* attribute macros */
+
+#define AHC_ATTR __attribute__((__always_inline__, __hot__, __const__))
+#define AH_ATTR __attribute__((__always_inline__, __hot__))
+#define HC_ATTR __attribute__((__hot__, __const__))
+#define H_ATTR __attribute__((__hot__))
+#define UNUSED __attribute__((__unused__))
+#define U_ATTR UNUSED
+
+#define D_ATTR(X) __attribute__((__unused__, __deprecated__("This is deprecated.\n" \
+ "Please use " X \
+ " instead")))
+
+#define UC_ATTR __attribute__((__unused__, __const__))
+#define UH_ATTR __attribute__((__unused__, __hot__))
+#define UHA_ATTR __attribute__((__unused__, __hot__, __always_inline__))
+
+/* types */
+
+typedef float Volume;
+typedef float Samples;
+typedef float Hertz;
+typedef float Seconds;
+typedef float Pulse;
+typedef float Beats;
+typedef float Semitones;
+typedef Pulse *Wave;
+
+typedef unsigned long LENT;
+
+typedef struct {
+ Wave w;
+ LENT n;
+} WaveFrag;
+
+/* supported instrument types */
+
+typedef enum {
+ PIANO,
+ DRUMKICK
+} InstrumentTypes;
+
+/* some helper functions */
+
+U_ATTR LENT max(LENT a, LENT b);
+
+U_ATTR LENT min(LENT a, LENT b);
+
+UNUSED long read_entire_file(const char *file_name, char **str);
+
+U_ATTR WaveFrag mix_waves(WaveFrag *frags, LENT len);
+
+HC_ATTR Hertz stoh(const Semitones st);
+
+#endif