想用c++写一个生成wav的程序。文件。过程就像,从一些txt文件中读取频率和长度,并根据这些参数计算样本,最后将它们写入wav。文件。我试过1通道和2通道的时候都可以,但是输入3通道就不行了(可能没有3通道的格式?),输入4通道就不行了
我不确定这应该是一个c++问题还是我误解了wav的结构。文件,这里是代码:
#include <iostream>
#include <fstream>
#include <sstream>
#include <cmath>
#include <vector>
#include "music.hxx"
const int sample_rate = 44100;
typedef struct WAV_HEADER {
uint8_t RIFF[4] = {'R', 'I', 'F', 'F'};
uint32_t ChunkSize;
uint8_t WAVE[4] = {'W', 'A', 'V', 'E'};
uint8_t fmt[4] = {'f', 'm', 't', ' '};
uint32_t SubChunk1Size = 16;
uint16_t AudioFormat = 1; // 1=PCM
uint16_t NumOfChan;
uint32_t SampleRate = sample_rate;
uint32_t BytesPerSec;
uint16_t BlockAlign;
uint16_t BitsPerSample = 8;
uint8_t SubChunk2ID[4] = {'d', 'a', 't', 'a'};
uint32_t SubChunk2Size;
} wav_hdr;
int8_t TriangleWave(int sample_rate, double frequency, int sample_index, double amp = 0.5){
double saw_wave = -1 + 2. * frequency * fmod(1./sample_rate * sample_index, 1./frequency); //Calculate sawtooth wave height
double tri_wave = 2 * fabs(saw_wave) - 1;
int8_t tri_wave_8bit = (int8_t) (tri_wave * 128 * amp); //8bit wave amp -128~127
return tri_wave_8bit;
}
int8_t SquareWave(int sample_rate, double frequency, int sample_index, double amp = 0.5){
double x = fmod(1./sample_rate * sample_index, 1./frequency);
if(x> (1./frequency*0.5)) return (int8_t)(-1. * 128 * amp);
else return (int8_t)(1. * 128 * amp);
}
int8_t NoiseWave(int sample_rate, double frequency, int sample_index, double amp = 0.){
double srand( time(0) );
double noise_wave = rand() / 32767. * 2. - 1.;
int8_t noise_wave_8bit = (int8_t) (noise_wave * 128 * amp);
return noise_wave_8bit;
}
double GetFrequency(const std::string& name){
if(name == "A_3") return A0;
else if(name == "B_3") return B_3;
else if(name == "C_3") return C_3;
else if(name == "D_3") return D_3;
else if(name == "E_3") return E_3;
else if(name == "F_3") return F_3;
else if(name == "G_3") return G_3;
else if(name == "G_3s") return G_3s;
else if(name == "A_2") return A_2;
else if(name == "B_2") return B_2;
else if(name == "C_2") return C_2;
else if(name == "D_2") return D_2;
else if(name == "E_2") return E_2;
else if(name == "F_2") return F_2;
else if(name == "G_2s") return G_2s;
else if(name == "A_1") return A_1;
else if(name == "B_1") return B_1;
else if(name == "C_1") return C_1;
else if(name == "D_1") return D_1;
else if(name == "E_1") return E_1;
else if(name == "F_1") return F_1;
else if(name == "G_1") return G_1;
else if(name == "G_1s") return G_1s;
else if(name == "A0") return A0;
else if(name == "B0") return B0;
else if(name == "C0") return C0;
else if(name == "D0") return D0;
else if(name == "E0") return E0;
else if(name == "F0") return F0;
else if(name == "G0") return G0;
else if(name == "A1") return A1;
else if(name == "0") return 1e-9;
else{
std::cerr<<name<<" is undefined tune"<<std::endl;
exit(0);
}
}
void ReadData(std::string file, const double LengthPerBeat, std::vector<std::vector<double>>& Tracks){
//Read score from txt files
std::vector<double> TrackFre1;
std::ifstream f_in(file, std::ios::in);
if(!f_in){
std::cerr<<"sound track file not found"<<std::endl;
}
std::string line;
std::stringstream ss;
std::string fre;
double nBeats = 0.;
double music_length = 0.;
int NumOfSamp = 0;
while(std::getline(f_in, line)){
ss.str(line);
ss>>fre>>nBeats;
ss.clear();
double start = music_length;
music_length += nBeats * LengthPerBeat;
int nSamples = (int)(music_length / (1./sample_rate)) - (int)(start / (1./sample_rate));
NumOfSamp += nSamples;
for(int i=0; i<nSamples; ++i){
TrackFre1.push_back( GetFrequency(fre) );
}
std::cout<<fre<<" "<<nBeats<<" "<<" "<<start<<" "<<nSamples<<std::endl;
}
Tracks.push_back(std::move(TrackFre1));
}
int main(int argc, char** argv){
if(argc<3){
std::cerr<<"./music <doc.txt> <soundTrack1.txt> <soundTrack2.txt> ..."<<std::endl;
return 1;
}
static_assert(sizeof(wav_hdr) == 44, "");
int nTracks = argc - 2; //First par is program itself and second is doc.txt
std::vector< std::vector<double> > Tracks;
//Read doc
std::string title;
std::string tempo_str;
std::string NumOfBars;
std::string shape;
std::vector<std::string> WaveShape;
std::ifstream f_doc(argv[1], std::ios::in);
if(!f_doc) return 1;
f_doc>>title>>tempo_str;
if(title!="tempo"){
std::cerr<<"tempo not found"<<std::endl;
exit(0);
}
f_doc>>title>>NumOfBars;
while(f_doc>>shape){
WaveShape.push_back(shape);
std::cout<<shape<<std::endl;
}
static const int tempo = atoi(tempo_str.c_str());
static const double LengthPerBeat = 60./tempo; //Sec
f_doc.close();
for(int i=0; i<(argc-2); ++i){
ReadData(argv[i+2], LengthPerBeat, Tracks);
}
// Write Header of wav file
wav_hdr wav;
wav.NumOfChan = nTracks;
std::cout<<wav.NumOfChan<<std::endl;
wav.BytesPerSec = wav.SampleRate * wav.NumOfChan;
wav.BlockAlign = wav.NumOfChan;
uint32_t data_size = Tracks.at(0).size() * wav.NumOfChan;//wav.BitsPerSample/8 * wav.SamplesPerSec;
wav.ChunkSize = data_size + sizeof(wav_hdr) - 8;
wav.SubChunk2Size = data_size;
std::ofstream out("test.wav", std::ios::binary);
out.write(reinterpret_cast<const char*>(&wav), sizeof(wav));
// Check the number of samples of sound tracks
std::cout<<Tracks.size()<<std::endl;
int8_t sample;
int nSamples = Tracks.at(0).size();
for(int i=0; i<Tracks.size(); ++i){
if(Tracks.at(i).size() > nSamples) continue;
else nSamples = Tracks.at(i).size();
}
// std::cout<<Tracks.at(0).size()<<" "<<Tracks.at(1).size()<<" "<<Tracks.at(2).size()<<" "<<Tracks.at(3).size()<<" "<<nSamples<<std::endl;
//Write data into wav file
for(int i=0; i<nSamples; ++i){
for(int j=0; j<nTracks; ++j){
std::cout<<j<<"th track now"<<std::endl;
if(WaveShape.at(j)=="triangle"){
sample = TriangleWave(sample_rate, Tracks.at(j).at(i), i);
std::cout<<j<<"th track at "<<i<<" tri"<<std::endl;
}
else if(WaveShape.at(j)=="noise"){
sample = NoiseWave(sample_rate, Tracks.at(j).at(i), i);
std::cout<<j<<"th track at "<<i<<" noise"<<std::endl;
}
else if(WaveShape.at(j)=="square"){
sample = SquareWave(sample_rate, Tracks.at(j).at(i), i);
std::cout<<j<<"th track at "<<i<<" square"<<std::endl;
}
out.write(reinterpret_cast<const char*>(&sample), sizeof(int8_t));
}
}
return 0;
}
txt 文件包含类似
G0 3