Skip to content

Instantly share code, notes, and snippets.

@tux21b
Created November 25, 2012 09:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tux21b/4142931 to your computer and use it in GitHub Desktop.
Save tux21b/4142931 to your computer and use it in GitHub Desktop.
//------------------------------------------------------------------------------
// 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 <prog-tutor-hack@iicm.tugraz.at>
//
// 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