Skip to content

Instantly share code, notes, and snippets.

@csukuangfj
Created April 22, 2019 11:14
Show Gist options
  • Save csukuangfj/c1d1d769606260d436f8674c30662450 to your computer and use it in GitHub Desktop.
Save csukuangfj/c1d1d769606260d436f8674c30662450 to your computer and use it in GitHub Desktop.
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;
}

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