Skip to content

Instantly share code, notes, and snippets.

@SteelPh0enix
Last active November 12, 2022 22:23
Show Gist options
  • Save SteelPh0enix/c8e2434c5074755d9d60caae376de821 to your computer and use it in GitHub Desktop.
Save SteelPh0enix/c8e2434c5074755d9d60caae376de821 to your computer and use it in GitHub Desktop.
Generate simple WAV audio file in C++
#include <algorithm>
#include <array>
#include <cinttypes>
#include <cmath>
#include <fstream>
#include <iostream>
#include <string_view>
#include <vector>
#include "wav_header.hpp"
namespace ranges = std::ranges;
constexpr unsigned DefaultSampleRate = 44100u;
std::vector<double> generate_sine(double const frequency, double const duration,
unsigned const samples_per_second) {
std::vector<double> samples{};
std::size_t const samples_count = static_cast<std::size_t>(duration * samples_per_second);
samples.resize(samples_count);
double const phi_step = (2.0 * M_PI * frequency) / samples_per_second;
double phi = 0.0;
for (auto& sample : samples) {
sample = std::sin(phi);
phi += phi_step;
}
return samples;
}
void save_as_wav(std::string_view filename, std::vector<uint8_t> const& samples,
WavHeader const& header) {
std::fstream wav_file(filename.data(),
std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
if (!wav_file.is_open()) {
std::cerr << "Could not open file '" << filename << "'!" << std::endl;
return;
}
auto const header_bytes = header.to_bytes();
wav_file.write(reinterpret_cast<char const*>(header_bytes.data()), header_bytes.size());
wav_file.write(reinterpret_cast<char const*>(samples.data()), samples.size());
}
int main() {
auto samples = generate_sine(2137, 2.0, DefaultSampleRate);
std::vector<uint8_t> samples_bytes{};
samples_bytes.reserve(samples.size());
ranges::transform(samples, std::back_inserter(samples_bytes),
[](auto const sample) { return static_cast<uint8_t>((sample + 1.0) * 127.5); });
WavHeader header{
.amount_of_samples = static_cast<uint32_t>(samples_bytes.size()),
.amount_of_channels = 1,
.samples_per_second = DefaultSampleRate,
.bits_per_sample = 8,
};
save_as_wav("output.wav", samples_bytes, header);
}
#pragma once
#include <array>
#include <cstdint>
template <typename Array>
constexpr void uint16_to_bytes_le(uint16_t value, Array& buffer, std::size_t offset) {
buffer[offset] = (value & 0xFFu);
buffer[offset + 1u] = (value & 0xFF00u) >> 8u;
}
template <typename Array>
constexpr void uint32_to_bytes_le(uint32_t value, Array& buffer, std::size_t offset) {
buffer[offset] = (value & 0xFFu);
buffer[offset + 1u] = (value & 0xFF00u) >> 8u;
buffer[offset + 2u] = (value & 0xFF0000u) >> 16u;
buffer[offset + 3u] = (value & 0xFF000000u) >> 24u;
}
struct WavHeader {
uint32_t amount_of_samples;
uint16_t amount_of_channels;
uint32_t samples_per_second;
uint16_t bits_per_sample;
auto to_bytes() const {
std::array<uint8_t, 44> header{};
uint16_t const bytes_per_sample = bits_per_sample / 8u;
uint32_t const amount_of_bytes_in_data =
amount_of_samples * amount_of_channels * bytes_per_sample;
uint32_t const byte_rate = samples_per_second * amount_of_channels * bytes_per_sample;
uint16_t const block_align = amount_of_channels * bytes_per_sample;
uint32_t const sub_chunk_1_size = 16;
uint32_t const sub_chunk_2_size = amount_of_bytes_in_data;
uint32_t const chunk_size = 4u + 8u + sub_chunk_1_size + 8u + sub_chunk_2_size;
// ChunkID - RIFF
header[0] = 'R';
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
// ChunkSize
uint32_to_bytes_le(chunk_size, header, 4);
// Format - WAVE
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
// SubChunk1ID
header[12] = 'f';
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
// SubChunk1Size
uint32_to_bytes_le(sub_chunk_1_size, header, 16);
// AudioFormat - 1 for PCM
uint16_to_bytes_le(1, header, 20);
// NumChannels
uint16_to_bytes_le(amount_of_channels, header, 22);
// SampleRate
uint32_to_bytes_le(samples_per_second, header, 24);
// ByteRate
uint32_to_bytes_le(byte_rate, header, 28);
// BlockAlign
uint16_to_bytes_le(block_align, header, 32);
// BitsPerSample
uint16_to_bytes_le(bits_per_sample, header, 34);
// SubChunk2ID
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
// SubChunk2Size
uint32_to_bytes_le(sub_chunk_2_size, header, 40);
return header;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment