Created
November 25, 2012 09:36
-
-
Save tux21b/4142931 to your computer and use it in GitHub Desktop.
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
//------------------------------------------------------------------------------ | |
// siren.c | |
// | |
// A small example program to demonstrate the handling of binary files. | |
// | |
// Compile with: | |
// gcc -Wall -lm -o siren siren.c | |
// | |
// Group: 11 study assistant Christoph Hack | |
// Authors: Christoph Hack <[email protected]> | |
// | |
// Latest Changes: 25.11.2012 (by Christoph Hack) | |
//------------------------------------------------------------------------------ | |
#include <math.h> | |
#include <stdarg.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
// error codes | |
enum { | |
SUCCESS, | |
ERROR_WRITE, // I/O error while writing to the output file | |
}; | |
// function prototypes | |
int writeWaveHeader(FILE *file, uint32_t num_samples); | |
int writeTone(FILE *file, uint32_t freq, uint32_t num_samples); | |
int reportError(int code, ...); | |
//------------------------------------------------------------------------------ | |
// main generates a WAVE file called "siren.wav" that plays two tones (466 Hz | |
// and 622 Hz) alternately for 10 seconds. | |
// | |
// @param argc not used | |
// @param argv not used | |
// | |
// @return either 0 or one of the error codes defined above | |
// | |
int main(int argc, char **argv) | |
{ | |
char *filename = "siren.wav"; | |
printf("Generating \"%s\"...\n", filename); | |
FILE *file = fopen(filename, "wb"); | |
if (file == NULL) | |
return reportError(ERROR_WRITE, filename); | |
uint32_t sample_rate = 44100; // 44.1 kHz | |
uint32_t num_samples = sample_rate * 10; | |
if (writeWaveHeader(file, num_samples) == ERROR_WRITE) | |
return reportError(ERROR_WRITE, filename); | |
int i; | |
for (i = 0; i < 5; i++) | |
{ | |
if (writeTone(file, 466, sample_rate) == ERROR_WRITE) | |
return reportError(ERROR_WRITE, filename); | |
if (writeTone(file, 622, sample_rate) == ERROR_WRITE) | |
return reportError(ERROR_WRITE, filename); | |
} | |
fclose(file); | |
return EXIT_SUCCESS; | |
} | |
//------------------------------------------------------------------------------ | |
// _WaveHeader_ is the structure of a very simple WAVE header that contains a | |
// RIFF block, a format block and a data block. | |
// | |
struct _WaveHeader_ | |
{ | |
char chunk_id_[4]; | |
uint32_t chunk_size_; | |
char riff_type_[4]; | |
char fmt_header_[4]; | |
uint32_t fmt_length_; | |
uint16_t fmt_tag_; | |
uint16_t channels_; | |
uint32_t sample_rate_; | |
uint32_t avg_bpp_; | |
uint16_t frame_size_; | |
uint16_t bits_per_sample_; | |
char data_header_[4]; | |
uint32_t data_length_; | |
} __attribute__((packed)); | |
//------------------------------------------------------------------------------ | |
// writeWaveHeader writes the header of a WAVE file that contains a single | |
// 16 bit mono PCM modulated audio track with 44.1 kHz (CD quality). | |
// | |
// @param file the output stream | |
// @param num_samples the number of samples of the data block. | |
// | |
// @return either SUCCESS or ERROR_WRITE | |
// | |
int writeWaveHeader(FILE *file, uint32_t num_samples) | |
{ | |
struct _WaveHeader_ header; | |
strcpy(header.chunk_id_, "RIFF"); | |
header.chunk_size_ = 2 * num_samples + 8 + 24 + 4; | |
strcpy(header.riff_type_, "WAVE"); | |
strcpy(header.fmt_header_, "fmt "); | |
header.fmt_length_ = 16; | |
header.fmt_tag_ = 1; // PCM | |
header.channels_ = 1; // mono | |
header.sample_rate_ = 44100; // 44.1 kHz | |
header.bits_per_sample_ = 16; // 16 bit | |
header.frame_size_ = header.channels_ * ((header.bits_per_sample_ + 7) / 8); | |
header.avg_bpp_ = header.sample_rate_ * header.frame_size_; | |
strcpy(header.data_header_, "data"); | |
header.data_length_ = 2 * num_samples; | |
if (fwrite(&header, sizeof(header), 1, file) != 1) | |
return ERROR_WRITE; | |
return SUCCESS; | |
} | |
//------------------------------------------------------------------------------ | |
// writeTone writes a serie of 16 bit samples to the file that osciliate with | |
// the given frequency. | |
// | |
// @param file the output stream | |
// @param freq the frequency of the tone (in Hz) | |
// @param num_samples the number of samples that should be written | |
// | |
// @return either 0 or ERROR_WRITE | |
// | |
int writeTone(FILE *file, uint32_t freq, uint32_t num_samples) | |
{ | |
uint32_t sample; | |
for (sample = 0; sample < num_samples; sample++) | |
{ | |
double t = ((sample + 1.0) / 44100.0) * freq; | |
uint16_t data = ((1 << 16) - 1) * (0.5 * sin(t * 2.0 * M_PI) + 0.5); | |
if (fwrite(&data, sizeof(data), 1, file) == 0) | |
return ERROR_WRITE; | |
} | |
return SUCCESS; | |
} | |
//------------------------------------------------------------------------------ | |
// reportError is a general purpose error formatting function that accepts | |
// a variable number of arguments (similar to printf). | |
// | |
// @param code the error code | |
// @param ... an arbitrary number of additional arguments | |
// | |
// @return the error code | |
// | |
int reportError(int code, ...) | |
{ | |
char *format; | |
switch (code) | |
{ | |
case ERROR_WRITE: | |
format = "Error: could not write file \"%s\".\n"; | |
break; | |
} | |
va_list args; | |
va_start(args, code); | |
vprintf(format, args); | |
va_end(args); | |
return code; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment