How to Generate a .wav File in C++11

December 28, 2021

Unless it's compressed, a WAV file generally consists of raw PCM data plus 44 bytes of header, and is a relatively easy format to generate. The following code represents a header for a 44.1kHz 16bit mono WAV file. By changing num_channels, you can make it a header for stereo. The length of the raw PCM data in bytes should be passed as a constructor parameter.

#include <cstdint>

struct WaveHeader {
  explicit WaveHeader(int32_t raw_length) {
    subchunk2_size = raw_length;
    chunk_size = raw_length + 36;
    byte_rate = sample_rate * num_channels * bits_per_sample / 8;
    block_align = num_channels * bits_per_sample / 8;
  }
  char chunk_id[4] = {'R', 'I', 'F', 'F'};
  int32_t chunk_size;
  char format[4] = {'W', 'A', 'V', 'E'};

  char subchunk1_id[4] = {'f', 'm', 't', ' '};
  int32_t subchunk1_size = 16;
  int16_t audio_format = 1;
  int16_t num_channels = 1;
  int32_t sample_rate = 44100;
  int32_t byte_rate;
  int16_t block_align;
  int16_t bits_per_sample = 16;

  char subchunk2_id[4] = {'d', 'a', 't', 'a'};
  int32_t subchunk2_size;
};

Usage

The following code generates test.wav, which contains 440Hz sine wave for 1 second.

#include <cmath>
#include <vector>

int main(int argc, char *argv[]) {
  const double pi = acos(-1);

  std::vector<int16_t> raw(44100);
  for (int i = 0; i < 44100; ++i) {
    double t = i / 44100.0;
    raw[i] = 8192 * sin(440.0 * 2.0 * pi * t);
  }

  WaveHeader header(sizeof(int16_t) * raw.size());
  FILE *fp = fopen("test.wav", "wb");
  fwrite(&header, sizeof(WaveHeader), 1, fp);
  fwrite(&raw[0], sizeof(int16_t), raw.size(), fp);
  fclose(fp);

  return 0;
}

References


Tetsui Ohkubo - peryaudo.org