122 lines
3.5 KiB
C
122 lines
3.5 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
const double PI = 3.141592653589793;
|
|
const double SECONDS = 0.5;
|
|
const size_t CHANNELS = 1;
|
|
const size_t SAMPLE_RATE = 44100;
|
|
const size_t BITS_PER_SAMPLE = 16;
|
|
const size_t TOTAL_SAMPLES = (SECONDS * CHANNELS * SAMPLE_RATE);
|
|
|
|
// http://soundfile.sapp.org/doc/WaveFormat/ (https://archive.is/069fj)
|
|
struct wav_header
|
|
{
|
|
char chunk_id[4];
|
|
uint32_t chunk_size;
|
|
char format[4];
|
|
|
|
char subchunk_id[4];
|
|
uint32_t subchunk1_size;
|
|
uint16_t audio_format;
|
|
uint16_t num_channels;
|
|
uint32_t sample_rate;
|
|
uint32_t byte_rate;
|
|
uint16_t block_align;
|
|
uint16_t bits_per_sample;
|
|
|
|
char subchunk2_id[4];
|
|
uint32_t subchunk2_size;
|
|
};
|
|
|
|
static void *xcalloc(const size_t num, const size_t size)
|
|
{
|
|
void *ptr = calloc(num, size);
|
|
if (ptr == NULL) {
|
|
perror("calloc");
|
|
exit(1);
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
void wav_populate_header(struct wav_header *const wavh, const size_t total_samples, const size_t channels)
|
|
{
|
|
memcpy(wavh->chunk_id, "RIFF", 4);
|
|
memcpy(wavh->format, "WAVE", 4);
|
|
memcpy(wavh->subchunk_id, "fmt ", 4);
|
|
|
|
wavh->chunk_size = (sizeof(struct wav_header) + total_samples) - 8;
|
|
wavh->subchunk1_size = 16;
|
|
wavh->audio_format = 1;
|
|
wavh->num_channels = 1;
|
|
wavh->sample_rate = SAMPLE_RATE;
|
|
wavh->byte_rate = (SAMPLE_RATE * channels * BITS_PER_SAMPLE) / 8;
|
|
wavh->block_align = (channels * BITS_PER_SAMPLE) / 8;
|
|
wavh->bits_per_sample = BITS_PER_SAMPLE;
|
|
|
|
memcpy(wavh->subchunk2_id, "data", 4);
|
|
wavh->subchunk2_size = (total_samples * channels * BITS_PER_SAMPLE) / 8;
|
|
}
|
|
|
|
void wav_create_file(const struct wav_header *wavh, FILE *wav_fp, const int16_t *data, const size_t total_samples)
|
|
{
|
|
fwrite(wavh, sizeof(struct wav_header), 1, wav_fp); // write WAV header to file
|
|
fwrite(data, sizeof(int16_t), total_samples, wav_fp); // ...and finally the audio data
|
|
}
|
|
|
|
int16_t sinewave(const size_t time, const size_t sample_rate, const double freq)
|
|
{
|
|
const double rad = (2 * PI) * (time / (sample_rate / freq)); // get radian frequency
|
|
const double t_amplitude = (32767 * 0.9);
|
|
return t_amplitude * sin(rad);
|
|
}
|
|
|
|
int16_t squarewave(const size_t time, const size_t sample_rate, const double freq)
|
|
{
|
|
const double t_amplitude = (32767 * 0.9);
|
|
const size_t period = (sample_rate / freq);
|
|
if ((time % period) < (period / 2))
|
|
return t_amplitude;
|
|
return 0;
|
|
//const double rad = (2 * PI) * (time / (sample_rate / freq)); // get radian frequency
|
|
//return ((sin(rad) >= 0) ? t_amplitude : 0); // -1 * t_amplitude for peak to peak amp.
|
|
//32767 * (2 * fabs(2 * (((time / period) - floor((time / period) + (1 / 2)))) ) * -1);
|
|
}
|
|
|
|
int16_t trianglewave(const size_t time, const size_t sample_rate, const double freq)
|
|
{
|
|
// a triangle wave can be expressed in terms of sine and arcsine
|
|
const double t_amplitude = (32767 * 0.9);
|
|
const double period = sin((2 * PI) * (time / (sample_rate / freq)));
|
|
return (2 * t_amplitude / PI) * asin(period);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
FILE *fp = stdout;
|
|
if (argc > 1) {
|
|
if ((fp = fopen(argv[1], "wb")) == NULL) {
|
|
perror("fopen");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int16_t buffer[TOTAL_SAMPLES];
|
|
const double freq = 220;
|
|
|
|
for (int i = 0; i < TOTAL_SAMPLES; ++i) {
|
|
buffer[i] = sinewave(i, SAMPLE_RATE, freq);
|
|
//buffer[i] = squarewave(i, SAMPLE_RATE, freq);
|
|
//buffer[i] = trianglewave(i, SAMPLE_RATE, freq);
|
|
}
|
|
|
|
struct wav_header *wavh = xcalloc(1, sizeof(struct wav_header));
|
|
wav_populate_header(wavh, TOTAL_SAMPLES, CHANNELS);
|
|
wav_create_file(wavh, fp, buffer, TOTAL_SAMPLES);
|
|
|
|
free(wavh);
|
|
fclose(fp);
|
|
return 0;
|
|
}
|