Created
April 22, 2019 11:14
-
-
Save csukuangfj/c1d1d769606260d436f8674c30662450 to your computer and use it in GitHub Desktop.
create wav file using c++
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
// 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; | |
} |
The line
wav.Subchunk2Size = fsize + sizeof(wav_hdr) - 44;
could be more simple...
wav.Subchunk2Size = fsize;
The line wav.Subchunk2Size = fsize + sizeof(wav_hdr) - 44; could be more simple... wav.Subchunk2Size = fsize;
Yes, you are right.
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
this code snippet shows how to create a .wav file for raw pcm data.