#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <algorithm>
#include <iomanip>
struct tag_fileenum {
std::string filename;
/* RIFF */
uint32_t totalSize;
/* WAVE */
/* fmt_ */
/*uint16_t formatID;*/
uint16_t channelNum;
uint32_t samplingRate;
uint32_t bytesPerSec;
uint16_t bytesBlockAlign;
uint16_t bitsPerSample;
/*data*/
uint32_t bytesDataNum;
uint32_t posDataStart;
};
uint32_t read_(std::fstream &fs, int n) {
uint32_t byte, data = 0;
for (int i = 0; i < n; i++) {
byte = 0;
fs.read((char *)&byte, 1);
if (!fs)
return 0;
for (int j = 0; j < i; j++)
byte = byte << 8;
data = data | byte;
}
return data;
}
int write_(std::fstream &fs, int n, uint32_t data) {
uint32_t byte;
for (int i = 0; i < n; i++) {
byte = 0xff & data;
fs.write((char *)&byte, 1);
if (!fs)
return 0;
data = data >> 8;
}
return n;
}
void wavFileCheckAndReadParam(std::fstream &fs, struct tag_fileenum *fileenum) {
uint32_t dummy, posDataStart = 0;
uint32_t n;
n = 4;
if ((dummy = read_(fs, n)) != 0x46464952) { /* RIFF */
std::cerr << "the file : " << fileenum->filename << " is not RIFF but " << std::setbase(16) << dummy << ", aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 4;
if ((fileenum->totalSize = read_(fs, n)) == 0) {
std::cerr << "cannot read : " << fileenum->filename << ", aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 4;
if ((dummy = read_(fs, n)) != 0x45564157) { /* WAVE */
std::cerr << "the file : " << fileenum->filename << " is not WAVE, aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 4;
if ((dummy = read_(fs, n)) != 0x20746D66) {
std::cerr << "the file : " << fileenum->filename << " has not format chunk partition, aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
uint32_t fmt_ChunkSize;
n = 4;
if ((fmt_ChunkSize = read_(fs, n)) < 16) {
std::cerr << "cannot read : " << fileenum->filename << ", aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
uint32_t extraFmt_ChunkSize = fmt_ChunkSize - 16;
n = 2;
if ((dummy = read_(fs, n)) != 0x0001) {
std::cerr << "the file : " << fileenum->filename << " is not PCM format, aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 2;
if ((fileenum->channelNum = read_(fs, n)) == 0) {
std::cerr << "cannot read : " << fileenum->filename << ", aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 4;
if ((fileenum->samplingRate = read_(fs, n)) != 0x0000ac44) {
std::cerr << "the file : " << fileenum->filename << "'s sampling rate is" << dummy << ", not CD-like, aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 4;
if ((fileenum->bytesPerSec = read_(fs, n)) == 0) {
std::cerr << "cannot read : " << fileenum->filename << ", aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 2;
if ((fileenum->bytesBlockAlign = read_(fs, n)) == 0) {
std::cerr << "cannot read : " << fileenum->filename << ", aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 2;
if ((fileenum->bitsPerSample = read_(fs, n)) == 0) {
std::cerr << "cannot read : " << fileenum->filename << ", aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
if (extraFmt_ChunkSize > 0) { dummy = read_(fs, extraFmt_ChunkSize); posDataStart += extraFmt_ChunkSize; }
n = 4;
if ((dummy = read_(fs, n)) != 0x61746164) {
std::cerr << "the file : " << fileenum->filename << " has not data chunk partition, aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
n = 4;
if ((fileenum->bytesDataNum = read_(fs, 4)) == 0) {
std::cerr << "cannot read : " << fileenum->filename << ", aborted." << std::endl;
fs.close();
exit(0);
}
posDataStart += n;
fileenum->posDataStart = posDataStart;
if ((fileenum->bitsPerSample/8) * fileenum->channelNum != fileenum->bytesBlockAlign) {
std::cerr << "bad parameter : " << fileenum->bitsPerSample << " "
<< fileenum->channelNum << " "
<< fileenum->bytesBlockAlign << ", aborted." << std::endl;
fs.close();
exit(0);
}
if (fileenum->samplingRate * fileenum->bytesBlockAlign != fileenum->bytesPerSec) {
std::cerr << "bad parameter : " << fileenum->samplingRate << " "
<< fileenum->bytesBlockAlign << " "
<< fileenum->bytesPerSec << ", aborted." << std::endl;
fs.close();
exit(0);
}
}
int32_t calcTotalDataSize(std::vector<tag_fileenum> input_files_elements) {
int32_t total = 0;
for (std::vector<tag_fileenum>::const_iterator p = input_files_elements.begin(); p != input_files_elements.end(); p++) {
total += p->bytesDataNum;
}
return total;
}
int32_t calcTotalSize(std::vector<tag_fileenum> input_files_elements) {
int32_t n = calcTotalDataSize(input_files_elements);
return n + 36; /* fix */
}
void makeOutputWaveFileHeader(std::fstream &fout, std::vector<tag_fileenum> input_files_elements, std::string file_output) {
int n;
int32_t totalSize, totalDataSize;
/* RIFF */
n = 4; if (write_(fout, n, 0x46464952) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
totalSize = calcTotalSize(input_files_elements);
n = 4; if (write_(fout, n, totalSize) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* WAVE */
n = 4; if (write_(fout, n, 0x45564157) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* fmt_ */
n = 4; if (write_(fout, n, 0x20746D66) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* fmt_ size */
n = 4; if (write_(fout, n, 16) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* linear PCM */
n = 2; if (write_(fout, n, 1) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* channnel = Stereo */
n = 2; if (write_(fout, n, 2) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* sampling rate = input_file[0]'s data */
n = 4; if (write_(fout, n, input_files_elements[0].samplingRate) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* dataNumPerSec = input_file[0]'s samplingRate * Stereo * 16bits*/
n = 4; if (write_(fout, n, input_files_elements[0].samplingRate * 4) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* blocksize = 2 * 2(Steleo 16bits) */
n = 2; if (write_(fout, n, 4) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* bitsPerSample = 16 */
n = 2; if (write_(fout, n, 16) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* 'data' */
n = 4; if (write_(fout, n, 0x61746164) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
/* data size */
totalDataSize = calcTotalDataSize(input_files_elements);
n = 4; if (write_(fout, n, totalDataSize) != n) { std::cerr << "cannot write: " << file_output <<", aborted" << std::endl; }
}
int const N = 65536;
int copyData(std::fstream &fin, std::fstream &fout, int32_t bytesDataNum, int16_t channelNum, int16_t bitsPerSample) {
if (channelNum != 2) {
std::cerr << "channel num: " << channelNum << ", not implemented, aborted." << std::endl;
fin.close(); fout.close(); exit(0);
}
if (bitsPerSample != 16) {
std::cerr << "data bits num: " << bitsPerSample << ", not implemented, aborted." << std::endl;
fin.close(); fout.close(); exit(0);
}
uint8_t buffer[N];
while (bytesDataNum > 0) {
int n;
fin.read((char *)&buffer, std::min(N, bytesDataNum));
if (fin.fail())
return 0;
n = fin.gcount();
fout.write((char *)&buffer, n);
if (fout.fail())
return 0;
bytesDataNum -= n;
}
return 1;
}
void body(std::string output_file, std::vector<std::string>input_files) {
struct tag_fileenum fileenum;
std::vector<tag_fileenum> input_files_elements;
/* enum input file */
for (std::vector<std::string>::iterator p = input_files.begin(); p != input_files.end(); p++) {
std::fstream fs;
fs.open(*p, std::ios::in | std::ios::binary);
if (!fs.is_open()) {
std::cerr << "cannot open the file: " << *p << ", aborted." << std::endl;
exit(1);
}
fileenum.filename = *p;
wavFileCheckAndReadParam(fs, &fileenum);
std::cout << "file: " << fileenum.filename << " "
<< "channel: " << fileenum.channelNum << " "
<< "bits: " << fileenum.bitsPerSample << std::endl;
input_files_elements.push_back(fileenum);
fs.close();
}
/* copy input files to output/make output's header */
std::fstream fout;
fout.open(output_file, std::ios::out | std::ios::binary);
if (!fout.is_open()) {
std::cerr << "cannot open the file for output: " << output_file << ", aborted." << std::endl;
exit(0);
}
makeOutputWaveFileHeader(fout, input_files_elements, output_file);
for (std::vector<tag_fileenum>::const_iterator p = input_files_elements.begin(); p != input_files_elements.end(); p++) {
/* copy input files to output/copy bodies */
std::fstream fin;
fin.open(p->filename, std::ios::in | std::ios::binary);
if (!fin) {
std::cerr << "cannot read open: " << p->filename << "." << std::endl;
fin.close(); fout.close(); exit(0);
}
fin.seekg(p->posDataStart);
std::cout << "copying: " << p->filename << "." << std::endl;
if (copyData(fin, fout, p->bytesDataNum, p->channelNum, p->bitsPerSample) == 0) {
std::cerr << "cannot write: " << output_file << " or cannot read: " << p->filename << "." << std::endl;
fin.close(); fout.close(); exit(0);
}
fin.close();
}
fout.close();
}
/*--------------------------------*/
void help(char *cmd) {
std::cerr << "usage: " << cmd << "output.wav input1.wav [input2.wav [...]]" << std::endl;
std::cerr << " " << cmd << " : concatenate .wav files to one output.wav." << std::endl;
}
int main(int argc, char *argv[]) {
if (argc <= 2) {
help(argv[0]);
exit(0);
}
std::string output_file(argv[1]);
std::vector<std::string> input_files;
for (int i = 2; i < argc; i++)
input_files.push_back(argv[i]);
sort(input_files.begin(), input_files.end());
std::ifstream dummy;
dummy.open(output_file, std::ios::in);
if (dummy) {
std::cerr << "file: " << output_file << " for output already exists" << std::endl;
exit(0);
}
body(output_file, input_files);
return 0;
}
/* end */