summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlara Modin <klarasmodin@gmail.com>2020-08-28 15:10:37 +0200
committerKlara Modin <klarasmodin@gmail.com>2020-08-28 15:10:37 +0200
commit627fa0e90abf64abb647339869b7a4c2954ece2f (patch)
tree35b8e96eec369277b9ff91ce4dbb0fcbcb485be0
initial commit
-rw-r--r--genwav.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/genwav.c b/genwav.c
new file mode 100644
index 0000000..c860ecf
--- /dev/null
+++ b/genwav.c
@@ -0,0 +1,307 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define PI 3.14159265358979323846
+#define D_LEN(H, L) (8*(L)/(H->bits_per_sample))
+
+enum inst_kind {
+ Static,
+ Coeff
+};
+
+struct wav_header {
+ uint8_t magic[4];
+ uint32_t file_size;
+ uint8_t file_type[4];
+ uint8_t format_chunk_marker[4];
+ uint32_t length_format_data;
+ uint16_t format_type;
+ uint16_t channels;
+ uint32_t sample_rate;
+ uint32_t bytes_per_second;
+ uint16_t bytes_per_sample_total;
+ uint16_t bits_per_sample;
+ uint8_t data_chunk_header[4];
+ uint32_t data_section_size;
+};
+
+struct instrument_part {
+ double (*mod_func)(double);
+ double frequency;
+ double amplitude;
+ enum inst_kind freq_kind;
+ enum inst_kind amp_kind;
+};
+
+struct instrument {
+ size_t size;
+ size_t n_parts;
+ struct instrument_part *parts;
+};
+
+void put_tone(struct wav_header *header, int16_t data[], double(*func)(double), double frequency, int16_t amplitude, uint32_t length);
+void mod_fm(struct wav_header *header, int16_t data[], double(*func)(double), double frequency, int16_t amplitude, uint32_t length);
+
+struct wav_header *header_init()
+{
+ struct wav_header header = {
+ .magic = "RIFF",
+ .file_size = 44,
+ .file_type = "WAVE",
+ .format_chunk_marker = "fmt ",
+ .length_format_data = 16,
+ .format_type = 1,
+ .channels = 1,
+ .sample_rate = 48000,
+ .bytes_per_second = 0,
+ .bytes_per_sample_total = 0,
+ .bits_per_sample = 16,
+ .data_chunk_header = "data",
+ .data_section_size = 0,
+ };
+
+ header.bytes_per_second = header.sample_rate * header.bits_per_sample * header.channels / 8;
+ header.bytes_per_sample_total = header.bits_per_sample * header.channels / 8;
+
+ struct wav_header *ret = malloc(sizeof(*ret));
+ *ret = header;
+ return ret;
+}
+
+struct instrument *instrument_init(size_t parts)
+{
+ struct instrument *inst = malloc(sizeof(struct instrument));
+ inst->size = parts;
+ inst->n_parts = 0;
+ inst->parts = malloc(sizeof(struct instrument_part)*inst->size);
+}
+
+struct instrument *instrument_add_part(struct instrument *inst, struct instrument_part *part)
+{
+ if (inst->n_parts >= inst->size) return NULL;
+ inst->parts[inst->n_parts++] = *part;
+}
+
+void put_instrument(struct wav_header *header, struct instrument *inst, double frequency, int16_t amplitude, int16_t data[], size_t length)
+{
+ if (inst->n_parts < 1) return;
+
+ int16_t *tmp = calloc(sizeof(*tmp), length);
+ struct instrument_part *part = &inst->parts[0];
+ double freq = part->freq_kind == Static ? part->frequency : part->frequency*frequency;
+ double amp = part->amp_kind == Static ? part->amplitude : part->amplitude*amplitude;
+ put_tone(header, tmp, part->mod_func, freq, amp, length);
+ for (int i = 1; i < inst->n_parts; ++i) {
+ part = &inst->parts[i];
+ freq = part->freq_kind == Static ? part->frequency : part->frequency*frequency;
+ amp = part->amp_kind == Static ? part->amplitude : part->amplitude*amplitude;
+ mod_fm(header, tmp, part->mod_func, freq, amp, length);
+ }
+ for (int i = 0; i < length; ++i) {
+ data[i] += tmp[i];
+ }
+ free(tmp);
+
+}
+
+void header_update(struct wav_header *header, uint32_t datasize)
+{
+ header->file_size += datasize;
+ header->data_section_size = datasize;
+ header->bytes_per_sample_total = header->channels * header->bits_per_sample / 8;
+ header->bytes_per_second = header->bytes_per_sample_total * header->sample_rate;
+}
+
+void mod_fm(struct wav_header *header, int16_t data[], double(*func)(double), double frequency, int16_t amplitude, uint32_t length)
+{
+ length = D_LEN(header,length);
+ if (frequency == 0) return;
+ for (int i = 0; i < length; i++) {
+ int16_t val = amplitude*(*func)(2.0*PI*(i+data[i])*(frequency/header->sample_rate));
+ if ((data[i] > 0 && data[i] + val < val) || (val > 0 && data[i] + val < data[i])) {
+ data[i] = 0x7fff;
+ } else if ((data[i] < 0 && data[i] + val > val) || (val < 0 && data[i] + val > data[i])) {
+ data[i] = 0xffff;
+ } else {
+ data[i] += val;
+ }
+ }
+}
+
+void put_tone(struct wav_header *header, int16_t data[], double(*func)(double), double frequency, int16_t amplitude, uint32_t length)
+{
+ length = D_LEN(header, length);
+ if (frequency == 0) return;
+ for (int i = 0; i < length; i++) {
+ int16_t val = amplitude*(*func)(2*PI*i*(frequency/header->sample_rate));
+ if ((data[i] > 0 && data[i] + val < val) || (val > 0 && data[i] + val < data[i])) {
+ data[i] = 0x7fff;
+ } else if ((data[i] < 0 && data[i] + val > val) || (val < 0 && data[i] + val > data[i])) {
+ data[i] = 0xffff;
+ } else {
+ data[i] += val;
+ }
+ }
+}
+
+void put_scale(struct wav_header *header, int16_t data[], double(*func)(double), double frequency, int16_t amplitude, uint32_t length)
+{
+ int split = 9;
+ put_tone(header, data, func, frequency, 0xfff, length/split);
+ put_tone(header, data+D_LEN(header,length)/split, func, 9*frequency/8, 0xfff, length/split);
+ put_tone(header, data+2*D_LEN(header,length)/split, func, 5*frequency/4, 0xfff, length/split);
+ put_tone(header, data+3*D_LEN(header,length)/split, func, 4*frequency/3, 0xfff, length/split);
+ put_tone(header, data+4*D_LEN(header,length)/split, func, 3*frequency/2, 0xfff, length/split);
+ put_tone(header, data+5*D_LEN(header,length)/split, func, 5*frequency/3, 0xfff, length/split);
+ put_tone(header, data+6*D_LEN(header,length)/split, func, 15*frequency/8, 0xfff, length/split);
+ put_tone(header, data+7*D_LEN(header,length)/split, func, 2*frequency, 0xfff, length/split);
+}
+
+void put_scale_general(struct wav_header *header, int16_t data[], double(*func)(double), double frequency, int16_t amplitude, uint32_t length, int divisions)
+{
+ for (int i = 0; i < divisions+1; ++i) {
+ put_tone(header, data+i*D_LEN(header,length)/(divisions+1), func, frequency*pow(2, i/(double)divisions), amplitude, length/((double)divisions+1));
+ }
+}
+
+unsigned int gcd(unsigned int a, unsigned int b)
+{
+ while (a != 0 && b != 0) {
+ if (a > b) {
+ a %= b;
+ } else {
+ b %= a;
+ }
+ }
+ return a > b ? a : b;
+}
+
+double sawtooth(double x) {
+ x = fmod(x, 2*PI);
+ return 2*x/(2*PI)-1;
+}
+
+double square(double x) {
+ x = fmod(x, 2*PI);
+ return x < PI ? -1 : 1;
+}
+
+double triangle(double x) {
+ x = fmod(x, 2*PI);
+ if (x < PI/2)
+ return 4*x/(2*PI);
+ else if (x < 3*PI/2)
+ return 1 - 4*x/(2*PI);
+ else
+ return 4*x/(2*PI)-1;
+}
+
+double half_sine(double x) {
+ x = fmod(x, 2*PI);
+ if (x < PI)
+ return sin(x);
+ return 0;
+}
+
+double pseudo_sawtooth(double x) {
+ x = fmod(x, PI);
+ if (x < PI/2)
+ return sin(x);
+ return 0;
+}
+
+double abs_sine(double x) {
+ x = fmod(x, PI);
+ return sin(x);
+}
+
+double linear(double x) {
+ return x;
+}
+
+void put_something(struct wav_header *header, int16_t data[], struct instrument *inst, double frequency, int16_t amp, size_t length, int divisions)
+{
+ size_t pos = 0;
+ int atmp = amp;
+ do {
+ if (getchar() == EOF) break;
+ unsigned char f, l1, l2, a1, a2;
+ l1 = getchar();
+ l2 = getchar();
+ if (l1 <= 32 || l2 <= 16) continue;
+ if (l1 == l2) l1--;
+ if (!l1) l1++;
+ if (!l2) l2++;
+ int dur = (gcd(l1,l2) * header->sample_rate/10) % (3*header->sample_rate/2);
+ if (pos+dur >= length || pos > pos+dur || dur > pos+dur)
+ break;
+ f = getchar();
+ double freq = frequency*pow(2,(f % (2*divisions))/(double)divisions);
+ a1 = getchar();
+ a2 = getchar();
+ if (a1 <= 16 || a2 <= 16) {
+ atmp = amp + a1 - a2;
+ amp = 0;
+ } else {
+ amp = atmp + a1 - a2;
+ }
+ printf("length=%ld, pos=%ld, dur=%d, freq=%f, amp=%d\n", length, pos, dur, freq, amp);
+ put_instrument(header, inst, freq, amp, data+D_LEN(header,pos), dur);
+ /*
+ //put_fm(header, &data[pos], &sin, freq, amp/16, &sin, freq*0.98, amp/16, dur);
+ int16_t *tmp = calloc(sizeof(*tmp), dur);
+ put_tone(header, tmp, &sin, 220, amp/64, dur);
+ mod_fm(header, tmp, &sin, freq, amp, dur);
+ for (int i = 0; i < dur; ++i) {
+ data[pos+i] += tmp[i];
+ }
+ free(tmp);
+ */
+ pos += dur;
+ } while (pos < length);
+}
+
+int main(int argc, char *argv[])
+{
+ char *file = "default.wav";
+ if (argc > 1)
+ file = argv[1];
+ FILE *out = fopen(file, "w");
+ struct wav_header *header = header_init();
+ size_t length = header->sample_rate*header->channels*header->bits_per_sample/8*20;
+ int16_t *data = calloc(length, sizeof(*data));
+ header_update(header, length);
+
+ struct instrument *inst = instrument_init(2);
+ struct instrument_part part = {.mod_func=&sin, .frequency=220, .amplitude=1/64.0, .amp_kind=Coeff};
+ instrument_add_part(inst, &part);
+ part.frequency=1;
+ part.amplitude=1;
+ part.mod_func=&sin;
+ part.freq_kind=Coeff;
+ part.amp_kind=Coeff;
+ instrument_add_part(inst, &part);
+
+ //put_instrument(header, inst, 440, 0x7ff, data, length);
+ //put_instrument(header, inst, 440*3/2, 0x7ff, data, length);
+
+ int frequency = 440;
+ //put_tone(header, data, &sin, frequency, 0x7ff, length/2);
+ //put_tone(header, data+D_LEN(header,length/2), &sin, frequency*2, 0x7ff, length/2);
+ //put_scale_general(header, data, &sin, frequency, 0x7ff, length, 12);
+ //put_scale(header, data, &sin, frequency, 0x7ff, length);
+ put_something(header, data, inst, frequency, 0x7ff, length, 24);
+ //put_something(header, data, inst, frequency*5/2, length, 0x7ff, 48);
+
+ fwrite(header, sizeof(*header), 1, out);
+ fwrite(data, sizeof(*data), length, out);
+ fclose(out);
+ free(header);
+ free(data);
+ free(inst->parts);
+ free(inst);
+
+ return 0;
+}