Skip to content

Instantly share code, notes, and snippets.

@csukuangfj
Created April 22, 2019 11:14
create wav file using c++
// author: fangjun kuang <csukuangfj at gmail dot com>
// date: Apr. 22, 2019
// refer to http://www.topherlee.com/software/pcm-tut-wavformat.html
#include <fstream>
#include <iostream>
typedef struct WAV_HEADER {
/* RIFF Chunk Descriptor */
uint8_t RIFF[4] = {'R', 'I', 'F', 'F'}; // RIFF Header Magic header
uint32_t ChunkSize; // RIFF Chunk Size
uint8_t WAVE[4] = {'W', 'A', 'V', 'E'}; // WAVE Header
/* "fmt" sub-chunk */
uint8_t fmt[4] = {'f', 'm', 't', ' '}; // FMT header
uint32_t Subchunk1Size = 16; // Size of the fmt chunk
uint16_t AudioFormat = 1; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM
// Mu-Law, 258=IBM A-Law, 259=ADPCM
uint16_t NumOfChan = 1; // Number of channels 1=Mono 2=Sterio
uint32_t SamplesPerSec = 16000; // Sampling Frequency in Hz
uint32_t bytesPerSec = 16000 * 2; // bytes per second
uint16_t blockAlign = 2; // 2=16-bit mono, 4=16-bit stereo
uint16_t bitsPerSample = 16; // Number of bits per sample
/* "data" sub-chunk */
uint8_t Subchunk2ID[4] = {'d', 'a', 't', 'a'}; // "data" string
uint32_t Subchunk2Size; // Sampled data length
} wav_hdr;
int main() {
static_assert(sizeof(wav_hdr) == 44, "");
std::string in_name = "test.bin"; // raw pcm data without wave header
std::ifstream in(in_name, std::ifstream::binary);
uint32_t fsize = in.tellg();
in.seekg(0, std::ios::end);
fsize = (uint32_t)in.tellg() - fsize;
in.seekg(0, std::ios::beg);
printf("file size: %u\n", fsize);
wav_hdr wav;
wav.ChunkSize = fsize + sizeof(wav_hdr) - 8;
wav.Subchunk2Size = fsize + sizeof(wav_hdr) - 44;
std::ofstream out("test.wav", std::ios::binary);
out.write(reinterpret_cast<const char *>(&wav), sizeof(wav));
int16_t d;
for (int i = 0; i < fsize; ++i) {
// TODO: read/write in blocks
in.read(reinterpret_cast<char *>(&d), sizeof(int16_t));
out.write(reinterpret_cast<char *>(&d), sizeof(int16_t));
}
return 0;
}
@csukuangfj
Copy link
Author

csukuangfj commented Apr 22, 2019

this code snippet shows how to create a .wav file for raw pcm data.

@RMKeene
Copy link

RMKeene commented Apr 18, 2022

The line
wav.Subchunk2Size = fsize + sizeof(wav_hdr) - 44;
could be more simple...
wav.Subchunk2Size = fsize;

@csukuangfj
Copy link
Author

The line wav.Subchunk2Size = fsize + sizeof(wav_hdr) - 44; could be more simple... wav.Subchunk2Size = fsize;

Yes, you are right.

@missblit
Copy link

missblit commented Jul 25, 2022

Thanks for this snippet. My TV's speaker automatically shut off every 20 minutes if no sounds are playing (modern technology was a mistake) so I used your code to write a program to run on startup to make quiet noises ever once in awhile to keep it awake.

I ended up with this (likely buggy, I mean I just needed it to make quiet noises)

#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <vector>
#include <windows.h>
#include <chrono>
#include <thread>
#include "intrin.h"

// PCM to WAV function copied from https://gist.github.com/csukuangfj/c1d1d769606260d436f8674c30662450
typedef struct WAVE_HDR {
  /* RIFF Chunk Descriptor */
  uint8_t RIFF[4] = {'R', 'I', 'F', 'F'}; // RIFF Header Magic header
  uint32_t ChunkSize;                     // RIFF Chunk Size
  uint8_t WAVE[4] = {'W', 'A', 'V', 'E'}; // WAVE Header
  /* "fmt" sub-chunk */
  uint8_t fmt[4] = {'f', 'm', 't', ' '}; // FMT header
  uint32_t Subchunk1Size = 16;           // Size of the fmt chunk
  uint16_t AudioFormat = 1; // Audio format 1=PCM,6=mulaw,7=alaw,     257=IBM
                            // Mu-Law, 258=IBM A-Law, 259=ADPCM
  uint16_t NumOfChan = 1;   // Number of channels 1=Mono 2=Sterio
  uint32_t SamplesPerSec = 16000;   // Sampling Frequency in Hz
  uint32_t bytesPerSec = 16000 * 2; // bytes per second
  uint16_t blockAlign = 2;          // 2=16-bit mono, 4=16-bit stereo
  uint16_t bitsPerSample = 16;      // Number of bits per sample
  /* "data" sub-chunk */
  uint8_t Subchunk2ID[4] = {'d', 'a', 't', 'a'}; // "data"  string
  uint32_t Subchunk2Size;                        // Sampled data length
} wav_hdr;

std::vector<uint8_t> PcmToWave(std::vector<uint16_t> pcm) {
  static_assert(sizeof(wav_hdr) == 44, "");

  auto fsize = pcm.size() / 2.0;
 
  wav_hdr wav;
  wav.ChunkSize = fsize + sizeof(wav_hdr) - 8;
  wav.Subchunk2Size = fsize + sizeof(wav_hdr) - 44;

  std::vector<uint8_t> output;
  output.resize(sizeof(wav) + pcm.size() * sizeof(pcm[0]));
  memcpy(output.data(), &wav, sizeof(wav));
  memcpy(output.data() + sizeof(wav), pcm.data(), pcm.size() * sizeof(pcm[0]));
  return output;
}

#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
int main() {
  uint16_t loudness = 0x000F; // How loud does this have to be to keep soundbar awake?
  auto noise_interval = std::chrono::minutes(15); // How frequent does this have to be to keep soundbar awake?
  std::vector<uint16_t> input;
  // A fun little sin wave :)
  for (int i = 0; i < 12*10000; ++i) {
    double wave = cos(i / 10000.0);
    uint16_t sample = loudness * ((wave + 1.0) / 2.0);
    input.push_back(_byteswap_ushort(sample));
  }
  auto result = PcmToWave(input);
  while (true) {
    std::this_thread::sleep_for(noise_interval);
    PlaySound(reinterpret_cast<LPCWSTR>(result.data()), nullptr, SND_MEMORY);
  }
  return 0;
}

@jan-ekholm
Copy link

Found this while looking for how to create a WAV file from in memory data. To me it looks like the last copying loop is wrong. In fsize you have the number of bytes in the source file, but in the last loop you read fsize number of uint16_t, i.e twice as much. Or am I reading this wrong?

@csukuangfj
Copy link
Author

Yes, you are right. The code is wrong.

Should be

  for (int i = 0; i < fsize / 2; ++i) {
    // TODO: read/write in blocks
    in.read(reinterpret_cast<char *>(&d), sizeof(int16_t));
    out.write(reinterpret_cast<char *>(&d), sizeof(int16_t));
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment