如何编写具有 4 个通道的 wav 文件[关闭]

问题描述 投票:0回答:0

想用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

c++ winapi wav
© www.soinside.com 2019 - 2024. All rights reserved.