简体   繁体   中英

How to generate a waveform of audio wav file using C++

I am currently using the C++ to implement my code. The idea is I want to read a wav file through a program and then output its waveform on the screen. I have already found some references there: C++ Reading the Data part of a WAV file . and here is my code of reading the audio file. I have no idea of how to generate the waveform .

#include <iostream>
#include <string>
#include <fstream>

using namespace std;
using std::string;
using std::fstream;



typedef struct  WAV_HEADER
{
   /* for the part of RIEF Chunk Descriptor */

   uint8_t             RIFF[4];        // RIFF Header      Magic header
   uint32_t            ChunkSize;      // RIFF Chunk Size  
   uint8_t             WAVE[4];        // WAVE Header  

   /* for the part of "fmt" subChunk */    
   uint8_t             fmt[4];         // FMT header       
   uint32_t            Subchunk1Size;  // Size of the fmt chunk                                
   uint16_t            AudioFormat;    // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM 
   uint16_t            NumOfChan;      // Number of channels 1=Mono 2=Sterio                   
   uint32_t             SamplesPerSec;  // Sampling Frequency in Hz                             
   uint32_t             bytesPerSec;    // bytes per second 
   uint16_t             blockAlign;     // 2=16-bit mono, 4=16-bit stereo 
   uint16_t             bitsPerSample;  // Number of bits per sample

   /* For the part of "data" subChunk */      
   uint8_t              Subchunk2ID[4]; // "data"  string   
   uint32_t             Subchunk2Size;  // Sampled data length    

}wav_hdr; 


int getFileSize(FILE* inFile);

int main(int argc, char* argv[])
{
wav_hdr wavHeader;
int headerSize = sizeof(wav_hdr), filelength = 0;

const char* filePath;
string input;
if (argc <= 1)
{
    cout << "Input wave file name: ";
    cin >> input;
    cin.get();
    filePath = input.c_str();
}
else
{
    filePath = argv[1];
    cout << "Input wave file name: " << filePath << endl;
}

FILE* wavFile = fopen(filePath, "r");
if (wavFile == nullptr)
{
    fprintf(stderr, "Unable to open wave file: %s\n", filePath);
    return 1;
}

//Read the header
size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile);
cout << "Header Read " << bytesRead << " bytes." << endl;
if (bytesRead > 0)
{
    //Read the data
    uint16_t bytesPerSample = wavHeader.bitsPerSample / 8;      //Number     of bytes per sample
    uint64_t numSamples = wavHeader.ChunkSize / bytesPerSample; //How many samples are in the wav file?
    static const uint16_t BUFFER_SIZE = 4096;
    int8_t* buffer = new int8_t[BUFFER_SIZE];
    while ((bytesRead = fread(buffer, sizeof buffer[0], BUFFER_SIZE / (sizeof buffer[0]), wavFile)) > 0)
    {
        /** DO SOMETHING WITH THE WAVE DATA HERE **/
        cout << "Read " << bytesRead << " bytes." << endl;
    }
    delete [] buffer;
    buffer = nullptr;
    filelength = getFileSize(wavFile);

    cout << "File is :" << filelength << " bytes." << endl;
    cout << "RIFF header :" << wavHeader.RIFF[0] << wavHeader.RIFF[1] << wavHeader.RIFF[2] << wavHeader.RIFF[3] << endl;
    cout << "WAVE header :" << wavHeader.WAVE[0] << wavHeader.WAVE[1] << wavHeader.WAVE[2] << wavHeader.WAVE[3] << endl;
    cout << "FMT :" << wavHeader.fmt[0] << wavHeader.fmt[1] << wavHeader.fmt[2] << wavHeader.fmt[3] << endl;
    cout << "Data size :" << wavHeader.ChunkSize << endl;

    // Display the sampling Rate from the header
    cout << "Sampling Rate :" << wavHeader.SamplesPerSec << endl;
    cout << "Number of bits used :" << wavHeader.bitsPerSample << endl;
    cout << "Number of channels :" << wavHeader.NumOfChan << endl;
    cout << "Number of bytes per second :" << wavHeader.bytesPerSec << endl;
    cout << "Data length :" << wavHeader.Subchunk2Size << endl;
    cout << "Audio Format :" << wavHeader.AudioFormat << endl;
    // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM

    cout << "Block align :" << wavHeader.blockAlign << endl;
    cout << "Data string :" << wavHeader.Subchunk2ID[0] << wavHeader.Subchunk2ID[1] << wavHeader.Subchunk2ID[2] << wavHeader.Subchunk2ID[3] << endl;


}
fclose(wavFile);
return 0;
}

// find the file size
int getFileSize(FILE* inFile)
{
    int fileSize = 0;
    fseek(inFile, 0, SEEK_END);

    fileSize = ftell(inFile);

    fseek(inFile, 0, SEEK_SET);
    return fileSize;
 }

I would have left this as a comment with code gists and not an answer since it doesn't answer your question directly but I don't have enough reputation for this.

I did what you're trying to do with Qt, most of my code comes from Qt's documentation. You need to find the peak value of the samples and then draw that. Let me share some code that could give you an idea on how you might want to do it.

void Waveform::appendSamples()
{
    buffer = audioDecoder->read();
    qreal peak = getPeakValue(buffer.format());
    const qint16 *data = buffer.constData<qint16>();
    int count = buffer.sampleCount() / 2;
    for (int i = 0; i < count; i += 1200){ // I want 40 samples per second currently assuming 48kHz
        double val = data[i]/peak;         
        samples.append(val * 300); // *300 for scaling
    }
}

qreal Waveform::getPeakValue(const QAudioFormat &format)
{
    qreal ret(0);
    if (format.isValid()){
        switch (format.sampleType()) {
            case QAudioFormat::Unknown:
            break;
            case QAudioFormat::Float:
                if (format.sampleSize() != 32)
                    ret = 0;
                else
                    ret = 1.00003;
            break;
            case QAudioFormat::SignedInt:
                if (format.sampleSize() == 32)
                    ret = INT_MAX;
                else if (format.sampleSize() == 16)
                    ret = SHRT_MAX;
                else if (format.sampleSize() == 8)
                    ret = CHAR_MAX;
                break;
            case QAudioFormat::UnSignedInt:
                if (format.sampleSize() == 32)
                    ret = UINT_MAX;
                else if (format.sampleSize() == 16)
                    ret = USHRT_MAX;
                else if (format.sampleSize() == 8)
                    ret = UCHAR_MAX;
            break;
        default:
            break;
        }
    }
    return ret;
}

For the drawing part in Qt (probably not useful to you)

QSGNode* WaveformRenderer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
    QSGSimpleRectNode *n = static_cast<QSGSimpleRectNode *>(oldNode);

    if(!n){
        n = new QSGSimpleRectNode();
        n->setColor(Qt::red);
    }

    QVector<double> samples = wav->getSamples(); //retrieve the samples

    int numberOfSamples = samples.size();

    setItemWidth(qreal(numberOfSamples * 2)); //signal stuff for qml

    for(int i = 0; i < numberOfSamples; ++i){
        QSGSimpleRectNode *temp = new QSGSimpleRectNode();
        temp->setColor(Qt::green);
        temp->setRect(i * 2, height()/2 - samples[i], 2, samples[i] * 2);
        n->appendChildNode(temp);
    }
    return n;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM