diff options
author | Klara Modin <klarasmodin@gmail.com> | 2020-08-28 15:10:37 +0200 |
---|---|---|
committer | Klara Modin <klarasmodin@gmail.com> | 2020-08-28 15:10:37 +0200 |
commit | 627fa0e90abf64abb647339869b7a4c2954ece2f (patch) | |
tree | 35b8e96eec369277b9ff91ce4dbb0fcbcb485be0 |
initial commit
-rw-r--r-- | genwav.c | 307 |
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; +} |