使用A4音扩展.WAV文件

时间:2017-11-04 18:50:28

标签: c++ audio

我的目标是了解如何在添加A4音和2000hz正弦波时延长.WAV文件。举个例子,我应该能够拍摄10秒长的.WAV文件并将其延长到30秒,同时将10秒以上的A4音增加到30秒标记,并在整个过程中添加2000Hz的正弦波。我目前的代码如下:

#include <iostream>
#include <fstream>
#include <stdint.h>
#include <cstdlib>
#include <ctime>
#include <math.h>

using namespace std;
//Structures for Headers
struct RIFFHeader
{
  char    chunkId[4]; // 4 byte character array
  uint32_t chunkSize; // 4 bytes
  char    format[4]; // 4 byte array 
};

struct FormatSubChunk
{
    char chunkId[4];
    uint32_t chunkSize; 
    uint16_t audioFormat;
    uint16_t channels;
    uint32_t frequency; //
    uint32_t byteRate;  //
    uint16_t blockAlign;
    uint16_t bitsPerSample;
};

struct DataSubChunkHeader
{
   char chunkId[4];
   uint32_t chunkSize; //
};

struct Sample
{
   uint16_t leftchannel;
   uint16_t rightchannel;
};
//------------------------------------------------



int main()
{

clock_t start;
double duration;
start = clock();

//Declaring input and output files
string infile = "Frederick_N_orig.wav";
string outfile = "Frederick_N_mod.wav";
ifstream in(infile.c_str(), ios::in | ios::binary);
ofstream out(outfile.c_str());

//Reading Headers
RIFFHeader RIFF;
in.read((char*)&RIFF,sizeof(RIFF));


FormatSubChunk Format;
in.read((char*)&Format,sizeof(Format));



DataSubChunkHeader Data;
in.read((char*)&Data,sizeof(Data));



RIFF.chunkSize = (RIFF.chunkSize - 46)/2 + 46;
Format.frequency = Format.frequency/2;
Format.byteRate = Format.byteRate/2;
Data.chunkSize = (Data.chunkSize-46)/2;


uint16_t clear = 0;

out.write((char*)&RIFF, sizeof(RIFF));
out.write((char*)&Format, sizeof(Format));
out.write((char*)&Data, sizeof(Data));
//---------------------------------------------

//Printing out Header information for troubleshooting
cout << "Chunk ID: " << RIFF.chunkId[0] << RIFF.chunkId[1] << RIFF.chunkId[2] << RIFF.chunkId[3] << endl;
cout << "Chunk Size: " << RIFF.chunkSize << endl;
cout << "Format: " << RIFF.format[0] << RIFF.format[1] << RIFF.format[2] << RIFF.format[3] <<endl;
cout << "Sub-chunk1 ID: " << Format.chunkId[0] << Format.chunkId[1] << Format.chunkId[2] << Format.chunkId[3] <<endl;
cout << "Sub-chunk1 Size: " << Format.chunkSize << endl;
cout << "Audio Format: " << Format.audioFormat << endl;
cout << "Number of Channels: " << Format.channels << endl;
cout << "Sample Rate: " << Format.frequency << endl;
cout << "Byte Rate: " << Format.byteRate << endl;
cout << "Block Align: " << Format.blockAlign << endl;
cout << "Bits Per Sample: " << Format.bitsPerSample << endl;
cout << "Sub-chunk2 ID: " << Data.chunkId[0] << Data.chunkId[1] << Data.chunkId[2] << Data.chunkId[3] << endl;
cout << "Sub-chunk2 Size: " << Data.chunkSize << endl << endl;
//------------------------------------------------------------------------------------------------------------------------
double p = 440;
float pie = 3.1415;


int16_t leftSample1;
int16_t leftSample2;
int16_t rightSample1;
int16_t rightSample2;
int32_t count = 0;

int n = 0;

//Reading in Left and Right Channels and performing processing
while(!in.eof())
{

    in.read((char*)&leftSample1, 2);
    if(in.eof())
        break;

    in.read((char*)&rightSample1, 2);
    if(in.eof())
        break;

    in.read((char*)&leftSample2, 2);
    if(in.eof())
        break;

    in.read((char*)&rightSample2, 2);
    if(in.eof())
        break;      

    const double max_amplitude = 32760;  // "volume"

    double hz        = 22050;    // samples per second
    double frequency = 440;  // middle C
    double seconds   = 21;      // time

    int N = hz * seconds;
    double amplitude = (double)n/N * max_amplitude;
    double value = sin(2*pie*p)*.25;

    int32_t leftAvg = ((int32_t)leftSample1 + (int32_t)leftSample2)/2;
    int32_t rightAvg = ((int32_t)rightSample1 + (int32_t)rightSample2)/2;
    leftAvg = leftAvg + leftAvg*value;
    rightAvg = rightAvg + rightAvg*value;

    n++;


    int16_t outLeft;
    int16_t outRight;

    if(leftAvg > 32767)
        outLeft = 32767;
    else if(leftAvg < -32768)
        outLeft = -32768;
    else
        outLeft = (int16_t)leftAvg;

    if(rightAvg > 32767)
        outRight = 32767;
    else if(rightAvg < -32768)
        outRight = -32768;
    else
        outRight = (int16_t)rightAvg;

    out.write((char*)&outLeft, sizeof(outLeft));
    out.write((char*)&outRight, sizeof(outRight));
    count++;

}
//--------------------------------------------------------------------------------------    
//cleaing up
in.close();
out.close();
//-------------

//Reading output file and comparing to original file
string infile1 = "Frederick_N_mod.wav";
ifstream in1(infile1.c_str(), ios::in | ios::binary);

RIFFHeader riff1;
in1.read((char*)&riff1,sizeof(riff1));


FormatSubChunk format1;
in1.read((char*)&format1,sizeof(format1));


//in.ignore(2);

DataSubChunkHeader data1;
in1.read((char*)&data1,sizeof(data1));
in.close();

cout << "Chunk ID: " << riff1.chunkId[0] << riff1.chunkId[1] << riff1.chunkId[2] << riff1.chunkId[3] << endl;
cout << "Chunk Size: " << riff1.chunkSize << endl;
cout << "Format: " << riff1.format[0] << riff1.format[1] << riff1.format[2] << riff1.format[3] <<endl;
cout << "Sub-chunk1 ID: " << format1.chunkId[0] << format1.chunkId[1] << format1.chunkId[2] << format1.chunkId[3] <<endl;
cout << "Sub-chunk1 Size: " << format1.chunkSize << endl;
cout << "Audio Format: " << format1.audioFormat << endl;
cout << "Number of Channels: " << format1.channels << endl;
cout << "Sample Rate: " << format1.frequency << endl;
cout << "Byte Rate: " << format1.byteRate << endl;
cout << "Block Align: " << format1.blockAlign << endl;
cout << "Bits Per Sample: " << format1.bitsPerSample << endl;
cout << "Sub-chunk2 ID: " << data1.chunkId[0] << data1.chunkId[1] << data1.chunkId[2] << data1.chunkId[3] << endl;
cout << "Sub-chunk2 Size: " << data1.chunkSize << endl << endl;
//---------------------------------------------------------------------------------------------------------------------------------

//Computing execution time and writing summary file
duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC;

string summaryoutfile = "summary.txt";
ofstream summaryoutput(summaryoutfile.c_str());
summaryoutput << "sampling frequency = " << Format.frequency << '\n';
long recordTime = count/Format.frequency;
summaryoutput << "record time = " << recordTime << " seconds" << '\n';
summaryoutput << "execution time = " << duration << " seconds" << '\n';
//----------------------------------------------------------------------------------------------

summaryoutput.close();

return 0;
}

所以,问题是,如何使用A4音扩展.WAV文件并添加2000hz正弦波?当我尝试上面的代码时,我得到一个乱码。任何帮助,将不胜感激!在此先感谢!!

1 个答案:

答案 0 :(得分:0)

这个问题有很多部分,所以我建议将问题分解成部分并验证每个部分是否有效。我还建议您使用像Audacity这样的音频编辑工具,以便检查结果并验证每个步骤是否正常。我将这个部分划分为:

  1. 代码是否正确输入了wav文件并以相同方式输出 如果你不做任何编辑?
  2. 然后你可以用持续时间的静音扩展wav文件 指定?
  3. 你能以特定的频率和采样率产生正弦音吗?
  4. 您可以将输入波数据和任何正弦音混合在一起 产生?
  5. 此操作非常粗略的伪代码:

    使用输入波形文件数据块大小+扩展大小

    计算输出波形文件数据夹头大小

    维护值leftValue和rightValue变量,这些变量是要写出的计算输出值

    对于从输出波形文件长度开始到结束波形文件长度的每个样本:

    • 将当前的leftValue和rightValues设置为0
    • 如果仍有剩余输入文件数据,请左右阅读 将样本值输入leftValue和rightValue
    • 如果要生成任何正弦音,请计算它们的值
      在给定的位置
    • 将生成的当前位置的正弦数据添加到leftValue和rightValue
    • 如果将多个数据源添加到一起,可能需要
      乘以一定的增益量,以便音频不会剪辑
    • 将leftValue和rightValue写入输出wav文件

    查看您当前的代码,我注意到了一些问题:

    输出波形文件数据卡盘大小的大小需要使用您正在扩展音频的输入波形文件数据块大小+大小来计算。另外,不要假设数据块之前的RIFF标题的长度在输入wav文件中是固定长度。

    我不确定为什么你一次两个读取输入样本并一起​​平均。这本身就会淹没任何音频数据。 (编辑:我看到你现在用这个逻辑对音频进行降频采样)

    您正在计算sin(2*pie*p)*.25,它们都具有固定值,并且永远不会随时间变化。在给定当前采样率的情况下,您需要计算某个位置的正弦值。然后,您将正弦运算的值乘以样本数据。您可能希望混合正弦数据,这应该是一个加法运算。

    使用

    等表达式严格限制您产生的振幅
    if(leftAvg > 32767)
            outLeft = 32767;
        else if(leftAvg < -32768)
            outLeft = -32768;
        else
            outLeft = (int16_t)leftAvg;
    

    如果水平超过极限,那么只会引入削波并产生不良结果。我还建议只计算所有样本作为范围-1.0到1.0的浮点值。在编辑音频与短值时,这是一种更容易管理的格式。

    编辑:添加了一个示例方法,该方法读取wav文件并使用指定长度的正弦音填充它。删除了下采样代码,因此逻辑更简单。

    void padWaveFile(string sourcePath, int paddingSeconds, float sineFrequency, string destinationPath)
    {
        ifstream in(sourcePath, ios::binary);
        ofstream out(destinationPath, ios::binary);
    
        RIFFHeader RIFF;    
        in.read((char*)&RIFF, sizeof(RIFF));
    
        FormatSubChunk Format;
        in.read((char*)&Format, sizeof(Format));
    
        DataSubChunkHeader Data;
        in.read((char*)&Data, sizeof(Data));
    
        float twoPi = 2 * 3.14159265358979f;
    
        int sourceSampleCount = Data.chunkSize / (Format.channels * Format.bitsPerSample / 8);
        int sampleRate = Format.frequency;
        int paddingSampleCount = paddingSeconds * sampleRate;
        int destinationSampleCount = sourceSampleCount + paddingSampleCount;    
    
        int sampleIndex = 0;
        float sinePosition = 0;                                         //Maintain position of sine for each sample
        float sineStep = (twoPi * sineFrequency) / (float)sampleRate;   //Sine sine step per sample at given sample rate and frequency
        float sineGain = 0.5;                                           //Attenuate sine by half so that it isnt at full volume in output
        int16_t maxShort = 32767;
        int16_t sineValue = 0;
    
        out.write((char*)&RIFF, sizeof(RIFF));
        out.write((char*)&Format, sizeof(Format));
    
        uint32_t destinationChunkSize = destinationSampleCount * Format.channels * Format.bitsPerSample;
        Data.chunkSize = destinationChunkSize;
        out.write((char*)&Data, sizeof(Data));
    
        int16_t inLeft;
        int16_t inRight;
        int16_t outLeft;
        int16_t outRight;
    
        if (Format.channels == 2) {
            for (int i = 0; i < destinationSampleCount; i++)
            {
                outLeft = 0;
                outRight = 0;
    
                if (!in.eof())
                {
                    in.read((char*)&inLeft, 2);
                    in.read((char*)&inRight, 2);
    
                    outLeft = inLeft;
                    outRight = inRight;
                }
                else {
                    sineValue = sin(sinePosition) * sineGain * maxShort;
    
                    outLeft = sineValue;
                    outRight = sineValue;
    
                    sinePosition += sineStep;
                }
    
                out.write((char*)&outLeft, sizeof(outLeft));
                out.write((char*)&outRight, sizeof(outRight));
    
                sampleIndex++;
            }
        }
    }