/** * Demonstration of generation of a WAV file with a generated * sine wave sound. * * More WAV format information can be found here: * * http://www.dragonwins.com/wav/ * * This code is hereby granted to the public domain. */ #include #include #include #include #define SOUND_DURATION (3.0) /* seconds */ #define FREQUENCY (440) /* Hz, 440 = Concert A */ #define VOLUME (0.2) /* 0.0 (silent) to 1.0 (max) */ #define CHANNELS (1) #define SAMPLES_PER_SECOND (11000) #define BITS_PER_SAMPLE (16) // make 8, 16, 24, or 32 // I thought I should be able to make BITS_PER_SAMPLE // anything I wanted, but I get bad playback for non-round // values. I'm confident I'm calculating bytes_per_sample // correctly, and I'm confident I'm calculating the volume // range correctly, so I'm not sure what gives. My player // seems to assume that the bits per sample is the bytes per // sample divided by 8, and maybe that's just true for WAV. #define RIFF_HEADER_SIZE (8) /* bytes */ /* RIFF chunk information structure: */ struct chunk { unsigned long size; /* size of the chunk not counting the header */ unsigned char *data; /* header and body of chunk */ }; /** * Write out a little-endian unsigned 32-bit value */ void little_endian_u32(unsigned char *buf, unsigned long v) { *(buf+0) = v&0xff; *(buf+1) = (v>>8)&0xff; *(buf+2) = (v>>16)&0xff; *(buf+3) = (v>>24)&0xff; } /** * Write out a little-endian unsigned 24-bit value */ void little_endian_u24(unsigned char *buf, unsigned long v) { *(buf+0) = v&0xff; *(buf+1) = (v>>8)&0xff; *(buf+2) = (v>>16)&0xff; } /** * Write out a little-endian unsigned 16-bit value */ void little_endian_u16(unsigned char *buf, unsigned int v) { *(buf+0) = v&0xff; *(buf+1) = (v>>8)&0xff; } /** * Construct the main RIFF chunk * * This is the outermost chunk which will encapsulate the WAV * subchunks. */ void make_riff_chunk(struct chunk *riff, struct chunk *fmt, struct chunk *data) { unsigned long total_size; riff->data = malloc(RIFF_HEADER_SIZE + 4); if (riff->data == NULL) { printf("failed to allocate RIFF chunk\n"); exit(1); } memcpy(riff->data, "RIFF", 4); riff->size = 4; total_size = riff->size + (fmt->size + RIFF_HEADER_SIZE) + (data->size + RIFF_HEADER_SIZE); little_endian_u32(riff->data+4, total_size); /* chunk size */ memcpy(riff->data+8, "WAVE", 4); /* RIFF type */ } /** * Construct the fmt subchunk * * This subchunk contains meta-information about the WAV file, number * of channels, data rate, etc. */ void make_fmt_chunk(struct chunk *fmt) { unsigned long data_rate = CHANNELS * SAMPLES_PER_SECOND; unsigned int bytes_per_sample = (BITS_PER_SAMPLE-1) / 8 + 1; unsigned int block_alignment = CHANNELS * bytes_per_sample; fmt->data = malloc(RIFF_HEADER_SIZE + 16); if (fmt->data == NULL) { printf("failed to allocate fmt chunk\n"); exit(1); } memcpy(fmt->data, "fmt ", 4); /* chunk type */ fmt->size = 16; // 18 for the 0 extra data size? little_endian_u32(fmt->data+4, fmt->size); /* size */ little_endian_u16(fmt->data+8, 1); /* comp type, 1==PCM */ little_endian_u16(fmt->data+10, CHANNELS); /* channels */ little_endian_u32(fmt->data+12, SAMPLES_PER_SECOND); /* slice rate */ little_endian_u32(fmt->data+16, data_rate); /* data rate */ little_endian_u16(fmt->data+20, block_alignment); /* block alignment */ little_endian_u16(fmt->data+22, BITS_PER_SAMPLE); /* sample depth */ //little_endian_u16(fmt->data+24, 0); /* extra data size */ } /** * Construct the data subchunk * * Contains the raw sound data */ void make_data_chunk(struct chunk *data) { unsigned long sample_count = SOUND_DURATION * SAMPLES_PER_SECOND; unsigned int bytes_per_sample = (BITS_PER_SAMPLE-1) / 8 + 1; unsigned long data_size = sample_count * bytes_per_sample * CHANNELS; unsigned char *p; unsigned long i; int channel; data->data = malloc(data_size + RIFF_HEADER_SIZE); if (data->data == NULL) { printf("failed to allocate data chunk\n"); exit(1); } memcpy(data->data, "data", 4); data->size = data_size; little_endian_u32(data->data+4, data_size); for (i = 0, p = data->data+8; i < sample_count; i++) { float freq = FREQUENCY; /* or try this! (instead of the above line) float freq = FREQUENCY + FREQUENCY * ((float)i / sample_count) ; */ /* figure out where we are in the sine wave: */ float a = 2*3.14159 * i / (SAMPLES_PER_SECOND / freq); /* calculate the sample value: */ float v = sin(a) * VOLUME; /* remap v from -1..1 to proper range */ int sample; if (BITS_PER_SAMPLE <= 8) { // unsigned in the range [0..max) long long range_top = (1LL << BITS_PER_SAMPLE) - 1; sample = ((v+1)/2)*range_top; } else { // signed in the range [-max/2..max/2) long long range_top = (1LL << BITS_PER_SAMPLE) - 1; long range_top2 = 1 << (BITS_PER_SAMPLE - 1); sample = ((long)(((v+1)/2)*range_top)) - range_top2; printf("%d\n", sample); } /* write sample to all channels: */ for (channel = 0; channel < CHANNELS; channel++) { switch (bytes_per_sample) { case 1: *p = sample; break; case 2: little_endian_u16(p, sample); break; case 3: little_endian_u24(p, sample); break; case 4: little_endian_u32(p, sample); break; default: fprintf(stderr, "bad bytes per sample!\n"); exit(2); } p += bytes_per_sample; } } } /** * Write out the WAV file */ void write_output(struct chunk *riff, struct chunk *fmt, struct chunk *data) { FILE *fp; fp = fopen("tone.wav", "wb"); if (fp == NULL) { printf("failed to open output file\n"); exit(2); } fwrite(riff->data, 1, RIFF_HEADER_SIZE + riff->size, fp); fwrite(fmt->data, 1, RIFF_HEADER_SIZE + fmt->size, fp); fwrite(data->data, 1, RIFF_HEADER_SIZE + data->size, fp); fclose(fp); } /** * Main */ int main(void) { struct chunk riff, fmt, data; make_data_chunk(&data); make_fmt_chunk(&fmt); make_riff_chunk(&riff, &fmt, &data); write_output(&riff, &fmt, &data); return 0; }