#include #include #include #include #define N_TYPES 5 #define DEF_NAME "out.wav" #define DEF_TYPE 1 #define DEF_FREQ 440.0 #define DEF_AMPL 0.5 #define DEF_HARM 4 #define DEF_RATE 44100 typedef unsigned char u8; typedef struct { char riff_magic[4]; int riff_size; char riff_fmt[4]; char fmt_magic[4]; int fmt_size; short audio_fmt; short n_channels; int sample_rate; int byte_rate; short block_align; short bits_per_sample; char data_magic[4]; int data_size; } wav_t; void make_wavhdr(wav_t *hdr, int rate, int length) { if (!hdr) return; const int bps = sizeof(float); const int n_ch = 1; int sz = length * bps * n_ch; memset(hdr, 0, sizeof(wav_t)); memcpy(hdr->riff_magic, "RIFF", 4); hdr->riff_size = sz + 36; memcpy(hdr->riff_fmt, "WAVE", 4); memcpy(hdr->fmt_magic, "fmt ", 4); hdr->fmt_size = 16; hdr->audio_fmt = 3; hdr->n_channels = n_ch; hdr->sample_rate = rate; hdr->byte_rate = rate * bps * n_ch; hdr->block_align = hdr->byte_rate / rate; hdr->bits_per_sample = bps * 8; memcpy(hdr->data_magic, "data", 4); hdr->data_size = sz; } u8 *open_wav(char *name, int rate, int start, int length) { if (!name || start < 0 || length < 1) return NULL; wav_t hdr; FILE *f = fopen(name, "rb"); int sz = 0; if (f) { fseek(f, 0, SEEK_END); sz = ftell(f); rewind(f); } if (!f || sz < 1) { make_wavhdr(&hdr, rate > 0 ? rate : DEF_RATE, length); u8 *temp = calloc(sizeof(wav_t) + length * sizeof(float), 1); memcpy(temp, &hdr, sizeof(wav_t)); return temp; } u8 *buf = malloc(sz); fread(buf, 1, sz, f); fclose(f); int data_sz = sz - sizeof(wav_t); if (data_sz < 1) { printf("Error: file is too small to be a WAV file\n"); free(buf); return NULL; } memcpy(&hdr, buf, sizeof(wav_t)); if (memcmp(hdr.riff_magic, "RIFF", 4) || memcmp(hdr.riff_fmt, "WAVE", 4) || memcmp(hdr.fmt_magic, "fmt ", 4)) { printf("Error: file is not a WAV file\n"); free(buf); return NULL; } if (hdr.data_size != data_sz) { printf("Error: invalid data size in header\n" " expected %d, got %d\n", data_sz, hdr.data_size); free(buf); return NULL; } if (hdr.audio_fmt != 3 || hdr.n_channels != 1) { printf("Error: unsupported WAV format\n" " this program expects the input WAV file to be\n" " mono and use floating-point samples\n"); free(buf); return NULL; } if (rate > 0 && hdr.sample_rate != rate) { printf("Error: sampling rate mismatch\n" " WAV sampling rate: %d, input sampling rate: %d\n", hdr.sample_rate, rate); free(buf); return NULL; } int data_len = data_sz / sizeof(float); if (data_len < start + length) { hdr.data_size = (start + length) * sizeof(float); hdr.riff_size = hdr.data_size + 36; buf = realloc(buf, sizeof(wav_t) + hdr.data_size); memset(buf + sizeof(wav_t) + data_sz, 0, hdr.data_size - data_sz); data_sz = hdr.data_size; data_len = data_sz / sizeof(float); } return buf; } void noise(float *buf, float freq, float ampl, int rate, int length) { FILE *f = fopen("/dev/urandom", "rb"); if (!f) return; unsigned short *rand = calloc(length, 2); fread(rand, 2, length, f); fclose(f); int i; for (i = 0; i < length; i++) buf[i] = ampl * (-1.0 + (float)rand[i] / 32767.5); free(rand); } void sawtooth(float *buf, float freq, float ampl, int rate, int length) { float sign = ampl < 0.0 ? -1.0 : 1.0; ampl *= sign; float grad = (ampl * 2) / ((float)rate / freq); float samp = -ampl; int i; for (i = 0; i < length; i++) { if (samp > ampl) samp -= ampl * 2; buf[i] += samp * sign; samp += grad; } } void sine(float *buf, float freq, float ampl, int rate, int length) { float period = 2 * M_PI * freq / (float)rate; int i; for (i = 0; i < length; i++) buf[i] += ampl * sin((float)i * period); } void square(float *buf, float freq, float ampl, int rate, int length) { float period = freq / (float)rate; int i; for (i = 0; i < length; i++) { int pos = (float)i * period; buf[i] += pos % 2 ? -ampl : ampl; } } void triangle(float *buf, float freq, float ampl, int rate, int length) { float sign = ampl < 0.0 ? -1.0 : 1.0; ampl *= sign; float grad = (ampl * 2) / ((float)rate / freq); float samp = -ampl; float inv = sign; int i; for (i = 0; i < length; i++) { if (samp > ampl) { samp -= ampl * 2; inv *= -1.0; } buf[i] += samp * inv; samp += grad; } } const char *wave_types = "nwiqt"; void (*generate[])(float *buf, float freq, float ampl, int rate, int length) = { noise, sawtooth, sine, square, triangle }; void print_usage(char *argv0) { printf("Invalid arguments\n" "Usage: %s \n" "\t-o \n" "\t-t \n" "\t\tw = sawtooth, i = sine, q = square, t = triangle\n" "\t-f \n" "\t-a \n" "\t-s \n" "\t-l \n" "\t-r \n", argv0); } int main(int argc, char **argv) { char *name = DEF_NAME; int type = DEF_TYPE; float freq = DEF_FREQ; float ampl = DEF_AMPL; int harm = DEF_HARM; int rate = DEF_RATE; int rate_set = 0; int i, j; for (i = 1; i < argc-1; i++) { if (strlen(argv[i]) != 2 || argv[i][0] != '-') { print_usage(argv[0]); return 1; } switch (argv[i][1]) { case 't': { type = -1; i++; int j; for (j = 0; j < N_TYPES; j++) { if (argv[i][0] == wave_types[j]) { type = j; break; } } if (type < 0) { printf("Error: invalid wave type '%c'\n", argv[i][0]); return 2; } break; } case 'o': name = argv[++i]; break; case 'f': freq = atof(argv[++i]); if (freq < 0.0) freq = DEF_FREQ; break; case 'a': ampl = atof(argv[++i]); if (ampl < -1.0 || ampl > 1.0) ampl = DEF_AMPL; break; case 'h': harm = atoi(argv[++i]); if (harm < 1) harm = DEF_HARM; break; case 'r': rate = atoi(argv[++i]); if (rate < 1) rate = DEF_RATE; else rate_set = 1; break; case 's': case 'l': break; default: print_usage(argv[0]); return 1; } } int start = 0; int length = rate; for (i = 1; i < argc-1; i++) { if (strlen(argv[i]) != 2 || argv[i][0] != '-') continue; if (argv[i][1] == 's') { start = (float)rate * atof(argv[++i]); break; } if (argv[i][1] == 'l') { length = (float)rate * atof(argv[++i]); break; } } u8 *buf = open_wav(name, rate_set ? rate : 0, start, length); if (!buf) return 3; float *data = (float*)(buf + sizeof(wav_t)); if (type >= 0 && type < N_TYPES) generate[type](data + start, freq, ampl, rate, length); wav_t *header = (wav_t*)buf; int size = sizeof(wav_t) + header->data_size; FILE *f = fopen(name, "wb"); fwrite(buf, 1, size, f); fclose(f); free(buf); return 0; }