dimanche 24 avril 2022

Decreasing Latency of playing sound using Playsound in C++ (windows)

Currently, we are playing 5 sounds one after another using Wave output and Fetching from the TCP socket. We are now using playBuffer to play the sounds. But there is a latency of playing one sound from another sound to. I don't want any latency in between playing the 5 audio and want to be played immediately. Is there any way to do that in playsound, or can I achieve that using any other library in C++ ? I am currently using a windows system. Would really appreciate some help, Seaching for hours for a solution.

// AudioTask.cpp : Defines the entry point for the console application. // Adapted from http://www.cplusplus.com/forum/beginner/88542/

#include "stdafx.h"




#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <mmsystem.h>
#include <iostream>
#include <fstream>
#include <conio.h>
#include <math.h> 
#include <stdint.h>

#define PI 3.14159265


using namespace std;

typedef struct WAV_HEADER1 {
    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;
    uint16_t NumOfChan = 1;
    uint32_t SamplesPerSec = 16000;
    uint32_t bytesPerSec = 16000 * 2;
    uint16_t blockAlign = 2;
    uint16_t bitsPerSample = 16;
    uint8_t Subchunk2ID[4]; // = { 'd', 'a', 't', 'a' };
    uint32_t Subchunk2Size;
} wav_hdr1;




void playBuffer(short* audioSamplesData1, short* audioSamplesData2, int count)
{
    static_assert(sizeof(wav_hdr1) == 44, "");

    wav_hdr1 wav;
    wav.NumOfChan = 2;
    wav.SamplesPerSec = 44100;
    wav.bytesPerSec = 176400;
    wav.blockAlign = 4;
    wav.bitsPerSample = 16;

    // Fixed values
    wav.RIFF[0] = 'R';
    wav.RIFF[1] = 'I';
    wav.RIFF[2] = 'F';
    wav.RIFF[3] = 'F';
    wav.WAVE[0] = 'W';
    wav.WAVE[1] = 'A';
    wav.WAVE[2] = 'V';
    wav.WAVE[3] = 'E';
    wav.fmt[0] = 'f';
    wav.fmt[1] = 'm';
    wav.fmt[2] = 't';
    wav.fmt[3] = ' ';
    wav.Subchunk2ID[0] = 'd';
    wav.Subchunk2ID[1] = 'a';
    wav.Subchunk2ID[2] = 't';
    wav.Subchunk2ID[3] = 'a';


    wav.ChunkSize = (count * 2 * 2) + sizeof(wav_hdr1) - 8;
    wav.Subchunk2Size = wav.ChunkSize - 20;

    char* data = new char[44 + (count * 2 * 2)];

    memcpy(data, &wav, sizeof(wav));
    int index = sizeof(wav);

    //constexpr double max_amplitude = 32766;

    for (int i = 0; i < count; i++)
    {
        short value = audioSamplesData1 ? audioSamplesData1[i] : 0;
        memcpy(data + index, &value, sizeof(short));
        index += sizeof(short);

        value = audioSamplesData2 ? audioSamplesData2[i] : 0;
        memcpy(data + index, &value, sizeof(short));
        index += sizeof(short);
    }
    PlaySound((char*)data, GetModuleHandle(0), SND_MEMORY | SND_SYNC);
}

void performAction(short audioSamplesData1[], short audioSamplesData2[], int count)
{
    playBuffer(audioSamplesData1, audioSamplesData1, count);
    playBuffer(audioSamplesData2, audioSamplesData2, count);
    playBuffer(audioSamplesData1, NULL, count);
    playBuffer(NULL, audioSamplesData2, count);
    playBuffer(audioSamplesData1, audioSamplesData2, count);
}


class Wave {

public:
    Wave(char * filename);
    ~Wave();
    void play(bool async = true);
    bool isok();

private:
    char * buffer;
    bool ok;
    HINSTANCE HInstance;
    int numberOfAudioBytes;
};

Wave::Wave(char * filename)
{
    ok = false;
    buffer = 0;
    HInstance = GetModuleHandle(0);
    numberOfAudioBytes = 0;

    ifstream infile(filename, ios::binary);

    if (!infile)
    {
        std::cout << "Wave::file error: " << filename << std::endl;
        return;
    }

    infile.seekg(0, ios::end);   // get length of file
    int length = infile.tellg();
    buffer = new char[length];    // allocate memory
    infile.seekg(0, ios::beg);   // position to start of file
    infile.read(buffer, length);  // read entire file

    std::cout << "Number of elements in buffer : " << length << std::endl;
    numberOfAudioBytes = length;

    infile.close();
    ok = true;
}

Wave::~Wave()
{
    PlaySound(NULL, 0, 0); // STOP ANY PLAYING SOUND
    delete[] buffer;      // before deleting buffer.
}

void Wave::play(bool async)
{
    if (!ok)
        return;


    // Create two arrays of sound data to use as a test for performing the task we need to do.
    const int SAMPLE_RATE = 44100; // 44.1 kHz
    const int FILE_LENGTH_IN_SECONDS = 3;
    const int NUMBER_OF_SAMPLES = SAMPLE_RATE*FILE_LENGTH_IN_SECONDS; // Number of elements of audio data in the array, 132300 in this case.

    std::cout << "NUMBER_OF_SAMPLES : " << NUMBER_OF_SAMPLES << std::endl;

    short audioSamplesData_A[NUMBER_OF_SAMPLES];
    short audioSamplesData_B[NUMBER_OF_SAMPLES];
    float maxVolume = 32767.0; // 2^15 - 10.0
    float frequencyHz_A = 500.0;
    float frequencyHz_B = 250.0;

    for (int i = 0; i < NUMBER_OF_SAMPLES; i++)
    {
        float pcmValue_A = sin(i*frequencyHz_A / SAMPLE_RATE * PI * 2);
        float pcmValue_B = sin(i*frequencyHz_B / SAMPLE_RATE * PI * 2);

        short pcmValueShort_A = (short)(maxVolume * pcmValue_A);
        short pcmValueShort_B = (short)(maxVolume * pcmValue_B);
        //short pcmValueShort_B = (short)(0.5*maxVolume*(pcmValue_A + pcmValue_B));

        audioSamplesData_A[i] = pcmValueShort_A; // This is what you need to play.
        audioSamplesData_B[i] = pcmValueShort_B; // This is what you need to play.

        // waveData += pack('h', pcmValueShort_A) - Python code from Python equivalent program, perhaps we need something similar.
        // See enclosed "Py Mono Stereo.py" file or visit https://swharden.com/blog/2011-07-08-create-mono-and-stereo-wave-files-with-python/
    }

    // The task that needs to be done for this project:
    // The audio data is available in the form of an array of shorts (audioSamplesData_A and audioSamplesData_B created above).
    // What needs to happen is this audio data (audioSamplesData_A and audioSamplesData_B) must each be played so we can hear them.
    // When this task is over, there will be no need for any WAV file anywhere, the goal is NOT to produce a WAV file. The goal is 
    // to take the audio data in the form of audioSamplesData_A and play it from memory somehow.
    // We need to take the input data (audioSamplesData_A and audioSamplesData_B) and play the same sounds that the 5 WAV files are currently playing, but
    // in the end, we will no longer need those WAV files.

    // You do NOT need to create any new files.
    // In the end, you do not need to read any files either.
    // In the final project, all you will need is this current main.cpp file. You run main.cpp and you hear the 5 sounds.
    // The 5 sounds, are created BY C++ here in this file (see loop above). 
    
    // Display the first 100 elements for one of the audio samples array
    for (int i = 0; i < 100; i++)
    {
        //std::cout << "i = " << i << ", audioSamplesData_B[i] : " << audioSamplesData_B[i] << std::endl;
    }

    // Display the first 100 elements for the serialized buffer of WAV header data + some audio data, all coming from one of the WAV files on the disk.
    for (int i = 0; i < 100; i++) // Last 6 elements is where audio data begins. First 44 elements are WAV header data.
    {
        //std::cout << "i = " << i << ", buffer[i] : " << (int) buffer[i] << std::endl;
    }
    performAction(audioSamplesData_A, audioSamplesData_B, NUMBER_OF_SAMPLES);

    // Play the sample sound, the one obtained from the WAV file on the disk, not the one created from the audio samples created above.
    //PlaySound((char*)(&audioSamplesData_A[0]), HInstance, SND_MEMORY | SND_SYNC);
    //PlaySound((char*)audioSamplesData_B, HInstance, SND_MEMORY | SND_SYNC);
    //PlaySound((char*)audioSamplesData_AB, HInstance, SND_MEMORY | SND_SYNC);
    //PlaySound((char*)buffer, HInstance, SND_MEMORY | SND_SYNC);
}
bool Wave::isok()
{
    return ok;
}
 

int main(int argc, char *argv[]) {

    std::cout << "Trying to play sound ...\n";

    // Load the WAV files from them from the disk. These files are here only to help you understand what we need. In the end, we will no longer need them.
    Wave outputA("outputA.WAV"); // Audio file equivalent to audioSamplesData_A curve generated in the loop above.
    Wave outputB("outputB.WAV"); // Audio file equivalent to audioSamplesData_B curve generated in the loop above.
    Wave outputALeftOnly("outputALeftOnly.WAV"); // Audio file that plays sound A on the left only, must be able to take audioSamplesData_A and somehow make it left only.
    Wave outputBRightOnly("outputBRightOnly.WAV"); // Audio file that plays sound B on the right only, must be able to take audioSamplesData_B and somehow make it right only.
    Wave outputALeftOutputBRight("outputALeftOutputBRight.WAV"); // Must be able to take both audioSamplesData_A and audioSamplesData_B and make it play different sounds in left and right.

    // Play the WAV files from the disk, either all of them or a subset of them.
    outputA.play(0);
    //outputB.play(0);
    //outputALeftOnly.play(0);
    //outputBRightOnly.play(0);
    //outputALeftOutputBRight.play(0);

    std::cout << "press key to exit";

    while (1) {} // Loop to prevent command line terminal from closing automatically.
    return 0;
}

Aucun commentaire:

Enregistrer un commentaire