Last active
August 18, 2024 23:19
-
-
Save PanagiotisPtr/bacbc4e4cbe9eabd32a540f6ef8ffae6 to your computer and use it in GitHub Desktop.
WAV File Reader / Writer 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
#pragma once | |
#include "Audio.h" | |
Audio::Audio(std::string str) { | |
if (str.substr(str.size() - 4) != ".wav") | |
throw std::invalid_argument("Can only read WAV files!"); | |
load_wav(str); | |
} | |
void Audio::load_wav(std::string str) { | |
FILE *fp; | |
errno_t err = fopen_s(&fp, str.c_str(), "rb"); | |
if (err != 0) | |
throw std::runtime_error("Error opening file!"); | |
// Chunk | |
fread(type, sizeof(char), 4, fp); | |
if (strcmp(type, "RIFF")) | |
throw std::runtime_error("Not a RIFF file!"); | |
fread(&ChunkSize, sizeof(int), 1, fp); | |
fread(format, sizeof(char), 4, fp); | |
if (strcmp(format, "WAVE")) | |
throw std::runtime_error("Not a WAVE format!"); | |
// 1st Subchunk | |
fread(Subchunk1ID, sizeof(char), 4, fp); | |
if (strcmp(Subchunk1ID, "fmt ")) | |
throw std::runtime_error("Missing fmt header!"); | |
fread(&Subchunk1Size, sizeof(int), 1, fp); | |
fread(&AudioFormat, sizeof(short), 1, fp); | |
fread(&NumChannels, sizeof(short), 1, fp); | |
fread(&SampleRate, sizeof(int), 1, fp); | |
fread(&ByteRate, sizeof(int), 1, fp); | |
fread(&BlockAlign, sizeof(short), 1, fp); | |
fread(&BitsPerSample, sizeof(short), 1, fp); | |
// 2nd Subchunk | |
fread(Subchunk2ID, sizeof(char), 4, fp); | |
if (strcmp(Subchunk2ID, "data")!=0) | |
throw std::runtime_error("Missing data header!"); | |
fread(&Subchunk2Size, sizeof(int), 1, fp); | |
// Data | |
//Subchunk2Size = NumSamples * NumChannels * BitsPerSample/8 | |
int NumSamples = Subchunk2Size / (NumChannels*(BitsPerSample / 8)); | |
data = std::vector<std::pair<short, short> >(NumSamples); | |
for (int i = 0; i < NumSamples; i++) { | |
fread(&data[i].first, sizeof(short), 1, fp); | |
fread(&data[i].second, sizeof(short), 1, fp); | |
} | |
fclose(fp); | |
} | |
void Audio::write_wav(std::string str) { | |
FILE *fp; | |
fopen_s(&fp, str.c_str(), "wb"); | |
fwrite(type, sizeof(char), 4, fp); | |
fwrite(&ChunkSize, sizeof(int), 1, fp); | |
fwrite(format, sizeof(char), 4, fp); | |
// 1st Subchunk | |
fwrite(Subchunk1ID, sizeof(char), 4, fp); | |
fwrite(&Subchunk1Size, sizeof(int), 1, fp); | |
fwrite(&AudioFormat, sizeof(short), 1, fp); | |
fwrite(&NumChannels, sizeof(short), 1, fp); | |
fwrite(&SampleRate, sizeof(int), 1, fp); | |
fwrite(&ByteRate, sizeof(int), 1, fp); | |
fwrite(&BlockAlign, sizeof(short), 1, fp); | |
fwrite(&BitsPerSample, sizeof(short), 1, fp); | |
// 2nd Subchunk | |
fwrite(Subchunk2ID, sizeof(char), 4, fp); | |
fwrite(&Subchunk2Size, sizeof(int), 1, fp); | |
// Ideally instead of the for loop there would be something like | |
// fwrite(&data[0], sizeof(short), data.size(), fp); | |
// But I have pairs and they have to be written interchangebly like right,left right,left right,left... | |
for (int i = 0; i < data.size(); i++) { | |
fwrite(&data[i].first, sizeof(short), 1, fp); | |
fwrite(&data[i].second, sizeof(short), 1, fp); | |
} | |
fclose(fp); | |
} |
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
#pragma once | |
//#define _CRT_SECURE_NO_DEPRECATE | |
#include <vector> | |
#include <utility> | |
#include <string> | |
#include <stdexcept> | |
#include <iostream> | |
#include <stdio.h> | |
#include <cstring> | |
class Audio { | |
public: | |
// Constructors | |
Audio(std::string str); | |
Audio() {}; | |
// Read / Write files | |
void load_wav(std::string str); | |
void write_wav(std::string str); | |
// Get functions | |
// Subchunk2Size = NumSamples * NumChannels * BitsPerSample / 8 <==> NumSamples = Subchunk2Size / (NumChannels*(BitsPerSample / 8)) | |
unsigned get_size() { return data.size(); } | |
unsigned get_sample_rate() { return SampleRate; } | |
unsigned get_n_channels() { return NumChannels; } | |
// Set Functions | |
void set_sample_rate(int n) { SampleRate = n; } | |
void set_n_channels(int n) { if (n != 1 && n != 2)throw std::invalid_argument("n can only be 1 (MONO) or 2 (STEREO)!"); NumChannels = n; } | |
// Overloaded Operators | |
std::pair<short, short> &operator[](unsigned i) { return data[i]; } | |
protected: | |
char type[5]; | |
char format[5]; | |
char Subchunk1ID[5]; | |
char Subchunk2ID[5]; | |
int ChunkSize; | |
int Subchunk1Size; | |
int SampleRate; | |
int ByteRate; | |
int Subchunk2Size; | |
short AudioFormat; | |
short NumChannels; | |
short BlockAlign; | |
short BitsPerSample; | |
// utility | |
unsigned NumSamples; | |
std::vector<std::pair<short, short> > data; | |
}; |
memset(format,0,sizeof(format));
memset(Subchunk1ID,0,sizeof(Subchunk1ID));
memset(Subchunk2ID,0,sizeof(Subchunk2ID));
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for reporting those issues. I have fixed the issue with
set_n_channels
. However, I don't plan to expand this gist any further. It's just some example code - it's not meant to be a library. For a more stable implementation, I would suggest using an audio library for C++.