Play wav file with PortAudio and libsndfile

136 views Asked by At

I'm writing a drum trigger detector in c++ with PortAudio and libsndfile. The part when the program detects the input and decides if the sound should be played is done but I have no idea how to implement the libsndfile version. The only thing I could do is to play the wav file with sndfile without reading the trigger input and without using callback function in a separate project but I'm not able to connect these two. The wav file should be played when trigLevel > 900.0f && legalHit is true. The program uses ASIO driver. If there is any other library for playing wav file and has ASIO support, I'm willing to switch.

#include <iostream>
#include <portaudio.h>
#include <vector>



const int SAMPLE_RATE = 44100;
const int FRAMES_PER_BUFFER = 256;

int audioCallback(const void* inputBuffer, void* outputBuffer,
    unsigned long framesPerBuffer,
    const PaStreamCallbackTimeInfo* timeInfo,
    PaStreamCallbackFlags statusFlags,
    void* userData);

int main() {

    PaError err;
    err = Pa_Initialize();
    if (err != paNoError) {
        std::cerr << "PortAudio error: " << Pa_GetErrorText(err) << std::endl;
        return 1;
    }

    PaDeviceIndex defaultInputDevice = Pa_GetDefaultInputDevice();
    if (defaultInputDevice == paNoDevice) {
        std::cerr << "No default input device found!" << std::endl;
        Pa_Terminate();
        return 1;
    }

    // Set up input parameters
    PaStreamParameters inputParameters;
    inputParameters.device = defaultInputDevice;
    inputParameters.channelCount = 1;
    inputParameters.sampleFormat = paFloat32;
    inputParameters.suggestedLatency = 0;
    inputParameters.hostApiSpecificStreamInfo = nullptr;

    int hostNr = Pa_GetHostApiCount();
    std::vector<const PaHostApiInfo*> infoVertex;
    for (int t = 0; t < hostNr; ++t) {
        infoVertex.push_back(Pa_GetHostApiInfo(t));
    }

    PaHostApiIndex defaultHostApi = Pa_GetDefaultHostApi(); // Get the index of the default host API

    std::cout << "Currently used audio driver:" << std::endl;
    std::cout << "  Name: " << infoVertex[defaultHostApi]->name << std::endl;
    std::cout << "  Type: " << infoVertex[defaultHostApi]->type << std::endl;
    std::cout << "  Device Count: " << infoVertex[defaultHostApi]->deviceCount << std::endl;
    std::cout << "---------------------------------------- " << std::endl;

    for (int t = 0; t < hostNr; ++t) {
        const PaHostApiInfo* hostApiInfo = infoVertex[t];
        std::cout << "Host API " << t << ":" << std::endl;
        std::cout << "  Name: " << hostApiInfo->name << std::endl;
        std::cout << "  Type: " << hostApiInfo->type << std::endl;
        std::cout << "  Device Count: " << hostApiInfo->deviceCount << std::endl;
        // You can access more fields from PaHostApiInfo if needed
        // For example: hostApiInfo->defaultInputDevice, hostApiInfo->defaultOutputDevice, etc.
        std::cout << std::endl;
    }

    PaStream* stream;
    err = Pa_OpenStream(&stream,
        &inputParameters,
        nullptr,
        SAMPLE_RATE,
        FRAMES_PER_BUFFER,
        paClipOff,
        audioCallback,
        nullptr);

    if (err != paNoError) {
        std::cerr << "PortAudio error: " << Pa_GetErrorText(err) << std::endl;
        Pa_Terminate();
        return 1;
    }

    err = Pa_StartStream(stream);
    if (err != paNoError) {
        std::cerr << "PortAudio error: " << Pa_GetErrorText(err) << std::endl;
        Pa_CloseStream(stream);
        Pa_Terminate();
        return 1;
    }


    std::cout << "Microphone level (normalized):" << std::endl;

    while (true) {
        Pa_Sleep(1000);
    }

    // Cleanup
    err = Pa_StopStream(stream);
    if (err != paNoError) {
        std::cerr << "PortAudio error: " << Pa_GetErrorText(err) << std::endl;
    }

    err = Pa_CloseStream(stream);
    if (err != paNoError) {
        std::cerr << "PortAudio error: " << Pa_GetErrorText(err) << std::endl;
    }

    Pa_Terminate();
    return 0;
}

// Audio callback function
int audioCallback(const void* inputBuffer, void* outputBuffer,
    unsigned long framesPerBuffer,
    const PaStreamCallbackTimeInfo* timeInfo,
    PaStreamCallbackFlags statusFlags,
    void* userData) {

    const float* input = static_cast<const float*>(inputBuffer);

    // RMS
    float trigLevel = 0.0f;
    for (unsigned long i = 0; i < framesPerBuffer; ++i) {
        trigLevel += input[i] * input[i];
    }
    trigLevel = std::sqrt(trigLevel / framesPerBuffer) * float(std::pow(10.0, 5));

    static bool legalHit = true;
    if (trigLevel > 900.0f && legalHit) {
        std::cout << "Trigger Input Level (RMS): " << trigLevel << std::endl;

        // This is where the sound should be played

        Sleep(50);

        legalHit = false;
    }
    else if (trigLevel < 900.0f)
    {
        legalHit = true;
    }

    return paContinue;
}
0

There are 0 answers