Last active
November 20, 2017 21:05
-
-
Save nkorth/f6682895b06373f6320a to your computer and use it in GitHub Desktop.
Wave generator
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
/* | |
* Wave generator | |
* by Nathan Korth | |
*/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <math.h> | |
#include <string.h> | |
#include <getopt.h> | |
#define PI 3.14159 | |
#define DEFAULT_LENGTH 1 | |
void writeWAV16(int16_t* samples, uint16_t numChannels, uint32_t numSamples, uint32_t sampleRate, FILE* stream){ | |
uint32_t formatChunkSize = 16; | |
uint32_t dataChunkSize = 4 * numChannels * numSamples; | |
uint32_t totalChunkSize = 4 + 8 + formatChunkSize + 8 + dataChunkSize; | |
uint16_t waveFormat = 1; // WAVE_FORMAT_PCM | |
uint32_t bytesPerSecond = sampleRate * 4 * numChannels; | |
uint16_t blockAlign = 4 * numChannels; | |
uint16_t bitsPerSample = 8 * 2; | |
fputs("RIFF", stream); | |
fwrite(&totalChunkSize, 4, 1, stream); | |
fputs("WAVE", stream); | |
// format chunk | |
fputs("fmt ", stream); | |
fwrite(&formatChunkSize, 4, 1, stream); | |
fwrite(&waveFormat, 2, 1, stream); | |
fwrite(&numChannels, 2, 1, stream); | |
fwrite(&sampleRate, 4, 1, stream); | |
fwrite(&bytesPerSecond, 4, 1, stream); | |
fwrite(&blockAlign, 2, 1, stream); | |
fwrite(&bitsPerSample, 2, 1, stream); | |
// data chunk | |
fputs("data", stream); | |
fwrite(&dataChunkSize, 4, 1, stream); | |
fwrite(samples, sizeof(int16_t), numSamples, stream); | |
} | |
void usage(char* invocation){ | |
fprintf(stderr, | |
"Usage: %s -f FREQUENCY [options] > filename.wav\n" | |
"Options:\n" | |
" -f FREQUENCY, --frequency=FREQUENCY Set frequency in Hz.\n" | |
" -h, --help Print this message and exit.\n" | |
" -l SECONDS, --length=SECONDS Set length in seconds. (default %d)\n" | |
" -w NAME, --waveform=NAME Set waveform to sine, saw, or square.\n", | |
invocation, DEFAULT_LENGTH); | |
} | |
double waveform_sine(double t){ | |
return sin(2 * PI * t); | |
} | |
double waveform_saw(double t){ | |
double intpart; // discarded | |
return -1 + 2 * modf(t, &intpart); | |
} | |
double waveform_square(double t){ | |
double intpart; | |
return modf(t, &intpart) > 0.5 ? -1 : 1; | |
} | |
int main(int argc, char* argv[]){ | |
int frequency = 0; | |
int length = DEFAULT_LENGTH; | |
double (*waveform)(double) = waveform_sine; | |
static struct option longopts[] = { | |
{"frequency", required_argument, NULL, 'f'}, | |
{"help", no_argument, NULL, 'h'}, | |
{"length", required_argument, NULL, 'l'}, | |
{"waveform", required_argument, NULL, 'w'} | |
}; | |
char flag; | |
while((flag = getopt_long(argc, argv, "f:hl:w:", longopts, NULL)) != -1){ | |
switch(flag){ | |
case 'f': | |
frequency = atoi(optarg); | |
break; | |
case 'h': | |
usage(argv[0]); | |
return 0; | |
case 'l': | |
length = atoi(optarg); | |
break; | |
case 'w': | |
if(strcmp(optarg, "sine") == 0){ | |
waveform = waveform_sine; | |
} else if(strcmp(optarg, "saw") == 0){ | |
waveform = waveform_saw; | |
} else if(strcmp(optarg, "square") == 0){ | |
waveform = waveform_square; | |
} else{ | |
fputs("Unrecognized waveform.\n", stderr); | |
return 1; | |
} | |
break; | |
} | |
} | |
if(frequency == 0 || length == 0){ | |
usage(argv[0]); | |
return 1; | |
} | |
int sampleRate = 44100; | |
uint32_t numSamples = sampleRate * length; | |
int16_t samples[numSamples]; | |
for(uint32_t i = 0; i < numSamples; ++i){ | |
double t = ((double) i) / sampleRate * frequency; | |
double sample_float = waveform(t); | |
samples[i] = sample_float * INT16_MAX; | |
} | |
writeWAV16(samples, 1, numSamples, sampleRate, stdout); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment