making car horn

There's only need for one forum for now. Discuss development. No advertising or thinly veiled attempts at advertising. Create a single thread for your project and be prepared to show and tell.
Post Reply
Mckl
Posts: 20
Joined: Wed Mar 04, 2020 12:09 am

making car horn

Post by Mckl »

Ok so I am trying to integrate code from tutorial on openal to my project just to make car sound horn:

So I've put error checking to header file:

Code: Select all

//
// Created by root on 29/2/20.
//

#pragma once

#ifndef ENGINE_SOUNDSYSTEM_H
#define ENGINE_SOUNDSYSTEM_H

#include <al.h>
#include <alc.h>
#include <Engine.h>
#include <fstream>
#include <string>
#include <vector>

#define alCall(function, ...) alCallImpl(__FILE__, __LINE__, function, __VA_ARGS__)
#define alcCall(function, device, ...) alcCallImpl(__FILE__, __LINE__, function, device, __VA_ARGS__)

namespace Engine {
    class SoundSystem : public Engine::SystemInterface {
        ALCdevice *device;
    public:
        void initialize() override;
        void update(Engine::deltaTime /*elapsed*/) override;
        
        bool Engine::SoundSystem::check_al_errors(const std::string& filename, const std::uint_fast32_t line)
        {
            ALenum error = alGetError();
            if (error != AL_NO_ERROR)
            {
                std::cerr << "***ERROR*** (" << filename << ": " << line << ")\n";
                switch (error)
                {
                case AL_INVALID_NAME:
                    std::cerr << "AL_INVALID_NAME: a bad name (ID) was passed to an OpenAL function";
                    break;
                case AL_INVALID_ENUM:
                    std::cerr << "AL_INVALID_ENUM: an invalid enum value was passed to an OpenAL function";
                    break;
                case AL_INVALID_VALUE:
                    std::cerr << "AL_INVALID_VALUE: an invalid value was passed to an OpenAL function";
                    break;
                case AL_INVALID_OPERATION:
                    std::cerr << "AL_INVALID_OPERATION: the requested operation is not valid";
                    break;
                case AL_OUT_OF_MEMORY:
                    std::cerr << "AL_OUT_OF_MEMORY: the requested operation resulted in OpenAL running out of memory";
                    break;
                default:
                    std::cerr << "UNKNOWN AL ERROR: " << error;
                }
                std::cerr << std::endl;
                return false;
            }
            return true;
        }

        template<typename alFunction, typename... Params>
        auto alCallImpl(const char* filename,
            const std::uint_fast32_t line,
            alFunction function,
            Params... params)
            ->typename std::enable_if_t<!std::is_same_v<void, decltype(function(params...))>, decltype(function(params...))>
        {
            auto ret = function(std::forward<Params>(params)...);
            check_al_errors(filename, line);
            return ret;
        }

        template<typename alFunction, typename... Params>
        auto alCallImpl(const char* filename,
            const std::uint_fast32_t line,
            alFunction function,
            Params... params)
            ->typename std::enable_if_t<std::is_same_v<void, decltype(function(params...))>, bool>
        {
            function(std::forward<Params>(params)...);
            return check_al_errors(filename, line);
        }



        bool Engine::SoundSystem::check_alc_errors(const std::string& filename, const std::uint_fast32_t line, ALCdevice* device)
        {
            ALCenum error = alcGetError(device);
            if (error != ALC_NO_ERROR)
            {
                std::cerr << "***ERROR*** (" << filename << ": " << line << ")\n";
                switch (error)
                {
                case ALC_INVALID_VALUE:
                    std::cerr << "ALC_INVALID_VALUE: an invalid value was passed to an OpenAL function";
                    break;
                case ALC_INVALID_DEVICE:
                    std::cerr << "ALC_INVALID_DEVICE: a bad device was passed to an OpenAL function";
                    break;
                case ALC_INVALID_CONTEXT:
                    std::cerr << "ALC_INVALID_CONTEXT: a bad context was passed to an OpenAL function";
                    break;
                case ALC_INVALID_ENUM:
                    std::cerr << "ALC_INVALID_ENUM: an unknown enum value was passed to an OpenAL function";
                    break;
                case ALC_OUT_OF_MEMORY:
                    std::cerr << "ALC_OUT_OF_MEMORY: an unknown enum value was passed to an OpenAL function";
                    break;
                default:
                    std::cerr << "UNKNOWN ALC ERROR: " << error;
                }
                std::cerr << std::endl;
                return false;
            }
            return true;
        }

        template<typename alcFunction, typename... Params>
        auto alcCallImpl(const char* filename,
            const std::uint_fast32_t line,
            alcFunction function,
            ALCdevice* device,
            Params... params)
            ->typename std::enable_if_t<std::is_same_v<void, decltype(function(params...))>, bool>
        {
            function(std::forward<Params>(params)...);
            return check_alc_errors(filename, line, device);
        }

        template<typename alcFunction, typename ReturnType, typename... Params>
        auto alcCallImpl(const char* filename,
            const std::uint_fast32_t line,
            alcFunction function,
            ReturnType& returnValue,
            ALCdevice* device,
            Params... params)
            ->typename std::enable_if_t<!std::is_same_v<void, decltype(function(params...))>, bool>
        {
            returnValue = function(std::forward<Params>(params)...);
            return check_alc_errors(filename, line, device);
        }




        std::int32_t convert_to_int(char* buffer, std::size_t len);
        bool load_wav_file_header(std::ifstream& file,
            std::uint8_t& channels,
            std::int32_t& sampleRate,
            std::uint8_t& bitsPerSample,
            ALsizei& size);
       
        char* load_wav(const std::string& filename,
            std::uint8_t& channels,
            std::int32_t& sampleRate,
            std::uint8_t& bitsPerSample,
            ALsizei& size);
        int playSound();
    
    };
}

#endif //ENGINE_SOUNDSYSTEM_H
and my cpp file looks like this:

Code: Select all

//
// Created by root on 29/2/20.
//

#include "SoundSystem.h"
#include "Sound.h"

void Engine::SoundSystem::initialize() {
   
    

}

void Engine::SoundSystem::update(Engine::deltaTime) {

    auto sounds = Engine::getStore().getRoot().getComponentsOfType<Component::Sound>();
    for (auto sound : sounds)
    {
        Engine::log<module, Engine::high>("Playing sound ", sound->name);
        playSound();
        Engine::getStore().getRoot().eraseComponent<Component::Sound>(sound->getId());
        
    }
}




std::int32_t Engine::SoundSystem::convert_to_int(char* buffer, std::size_t len)
{
    std::int32_t a = 0;
    //if (std::endian::native == std::endian::little)
    std::memcpy(&a, buffer, len);
    // else
      //  for (std::size_t i = 0; i < len; ++i)
       //    reinterpret_cast<char*>(&a)[3 - i] = buffer[i];
    return a;
}

bool Engine::SoundSystem::load_wav_file_header(std::ifstream& file,
    std::uint8_t& channels,
    std::int32_t& sampleRate,
    std::uint8_t& bitsPerSample,
    ALsizei& size)
{
    char buffer[4];
    if (!file.is_open())
        return false;

    // the RIFF
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read RIFF" << std::endl;
        return false;
    }
    if (std::strncmp(buffer, "RIFF", 4) != 0)
    {
        std::cerr << "ERROR: file is not a valid WAVE file (header doesn't begin with RIFF)" << std::endl;
        return false;
    }

    // the size of the file
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read size of file" << std::endl;
        return false;
    }

    // the WAVE
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read WAVE" << std::endl;
        return false;
    }
    if (std::strncmp(buffer, "WAVE", 4) != 0)
    {
        std::cerr << "ERROR: file is not a valid WAVE file (header doesn't contain WAVE)" << std::endl;
        return false;
    }

    // "fmt/0"
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read fmt/0" << std::endl;
        return false;
    }

    // this is always 16, the size of the fmt data chunk
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read the 16" << std::endl;
        return false;
    }

    // PCM should be 1?
    if (!file.read(buffer, 2))
    {
        std::cerr << "ERROR: could not read PCM" << std::endl;
        return false;
    }

    // the number of channels
    if (!file.read(buffer, 2))
    {
        std::cerr << "ERROR: could not read number of channels" << std::endl;
        return false;
    }
    channels = convert_to_int(buffer, 2);

    // sample rate
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read sample rate" << std::endl;
        return false;
    }
    sampleRate = convert_to_int(buffer, 4);

    // (sampleRate * bitsPerSample * channels) / 8
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read (sampleRate * bitsPerSample * channels) / 8" << std::endl;
        return false;
    }

    // ?? dafaq
    if (!file.read(buffer, 2))
    {
        std::cerr << "ERROR: could not read dafaq" << std::endl;
        return false;
    }

    // bitsPerSample
    if (!file.read(buffer, 2))
    {
        std::cerr << "ERROR: could not read bits per sample" << std::endl;
        return false;
    }
    bitsPerSample = convert_to_int(buffer, 2);

    // data chunk header "data"
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read data chunk header" << std::endl;
        return false;
    }
    if (std::strncmp(buffer, "data", 4) != 0)
    {
        std::cerr << "ERROR: file is not a valid WAVE file (doesn't have 'data' tag)" << std::endl;
        return false;
    }

    // size of data
    if (!file.read(buffer, 4))
    {
        std::cerr << "ERROR: could not read data size" << std::endl;
        return false;
    }
    size = convert_to_int(buffer, 4);

    /* cannot be at the end of file */
    if (file.eof())
    {
        std::cerr << "ERROR: reached EOF on the file" << std::endl;
        return false;
    }
    if (file.fail())
    {
        std::cerr << "ERROR: fail state set on the file" << std::endl;
        return false;
    }

    return true;
}

char* Engine::SoundSystem::load_wav(const std::string& filename,
    std::uint8_t& channels,
    std::int32_t& sampleRate,
    std::uint8_t& bitsPerSample,
    ALsizei& size)
{
    std::ifstream in(filename, std::ios::binary);
    if (!in.is_open())
    {
        std::cerr << "ERROR: Could not open \"" << filename << "\"" << std::endl;
        return nullptr;
    }
    
    if (!load_wav_file_header(in, channels, sampleRate, bitsPerSample, size))
    {
        std::cerr << "ERROR: Could not load wav header of \"" << filename << "\"" << std::endl;
        return nullptr;
    }

    char* data = new char[size];

    in.read(data, size);

    return data;
}

int Engine::SoundSystem::playSound()
{
    {
        ALCdevice* openALDevice = alcOpenDevice(nullptr);
        if (!openALDevice)
            return 0;

        ALCcontext* openALContext;
        //ALCint* attrlist = new int[1,1,1,1,1];
        if (!alcCall(alcCreateContext, openALContext, openALDevice, openALDevice, nullptr) || !openALContext)
        {
            std::cerr << "ERROR: Could not create audio context" << std::endl;
            return 0;
        }
        ALCboolean contextMadeCurrent = false;
        if (!alcCall(alcMakeContextCurrent, contextMadeCurrent, openALDevice, openALContext)
            || contextMadeCurrent != ALC_TRUE)
        {
            std::cerr << "ERROR: Could not make audio context current" << std::endl;
            return 0;
        }

        std::uint8_t 	channels;
        std::int32_t 	sampleRate;
        std::uint8_t 	bitsPerSample;
        ALsizei			dataSize;
        char* rawSoundData = load_wav("Assets/Sounds/CARHORN4.wav", channels, sampleRate, bitsPerSample, dataSize);
        if (rawSoundData == nullptr || dataSize == 0)
        {
            std::cerr << "ERROR: Could not load wav" << std::endl;
            return 0;
        }
        std::vector<char> soundData(rawSoundData, rawSoundData + dataSize);

        ALuint buffer;
        alCall(alGenBuffers, 1, &buffer);

        ALenum format;
        if (channels == 1 && bitsPerSample == 8)
            format = AL_FORMAT_MONO8;
        else if (channels == 1 && bitsPerSample == 16)
            format = AL_FORMAT_MONO16;
        else if (channels == 2 && bitsPerSample == 8)
            format = AL_FORMAT_STEREO8;
        else if (channels == 2 && bitsPerSample == 16)
            format = AL_FORMAT_STEREO16;
        else
        {
            std::cerr
                << "ERROR: unrecognised wave format: "
                << channels << " channels, "
                << bitsPerSample << " bps" << std::endl;
            return 0;
        }

        alCall(alBufferData, buffer, format, soundData.data(), soundData.size(), sampleRate);
        soundData.clear(); // erase the sound in RAM

        ALuint source;
        alCall(alGenSources, 1, &source);
        alCall(alSourcef, source, AL_PITCH, 1);
        alCall(alSourcef, source, AL_GAIN, 1.0f);
        alCall(alSource3f, source, AL_POSITION, 0, 0, 0);
        alCall(alSource3f, source, AL_VELOCITY, 0, 0, 0);
        alCall(alSourcei, source, AL_LOOPING, AL_FALSE);
        alCall(alSourcei, source, AL_BUFFER, buffer);

        alCall(alSourcePlay, source);

        ALint state = AL_PLAYING;

        while (state == AL_PLAYING)
        {
            alCall(alGetSourcei, source, AL_SOURCE_STATE, &state);
        }

        alCall(alDeleteSources, 1, &source);
        alCall(alDeleteBuffers, 1, &buffer);

        alcCall(alcMakeContextCurrent, contextMadeCurrent, openALDevice, nullptr);
        alcCall(alcDestroyContext, openALDevice, openALContext);

        ALCboolean closed;
        alcCall(alcCloseDevice, closed, openALDevice, openALDevice);

        return 0;
    }
}

Each time I press space bar game freezing while sound playing and then exception is thrown maybe you can see what I do wrong and help me fix it.
User avatar
Deckhead
Site Admin
Posts: 128
Joined: Fri Feb 21, 2020 5:44 am
Location: Sydney, Australia
Contact:

Re: making car horn

Post by Deckhead »

I edited your post for formatting.

The first thing you want to do is move your initialisation of OpenAL Device and Context into your Initialise method.

Code: Select all

        ALCdevice* openALDevice = alcOpenDevice(nullptr);
        if (!openALDevice)
            return 0;

        ALCcontext* openALContext;
        //ALCint* attrlist = new int[1,1,1,1,1];
        if (!alcCall(alcCreateContext, openALContext, openALDevice, openALDevice, nullptr) || !openALContext)
        {
            std::cerr << "ERROR: Could not create audio context" << std::endl;
            return 0;
        }
        ALCboolean contextMadeCurrent = false;
        if (!alcCall(alcMakeContextCurrent, contextMadeCurrent, openALDevice, openALContext)
            || contextMadeCurrent != ALC_TRUE)
        {
            std::cerr << "ERROR: Could not make audio context current" << std::endl;
            return 0;
        }
 
This code should only occur once when your program starts. You then want to move the sound loading into it's own function, something like load_sound

Code: Select all

        std::uint8_t 	channels;
        std::int32_t 	sampleRate;
        std::uint8_t 	bitsPerSample;
        ALsizei			dataSize;
        char* rawSoundData = load_wav("Assets/Sounds/CARHORN4.wav", channels, sampleRate, bitsPerSample, dataSize);
        if (rawSoundData == nullptr || dataSize == 0)
        {
            std::cerr << "ERROR: Could not load wav" << std::endl;
            return 0;
        }
        std::vector<char> soundData(rawSoundData, rawSoundData + dataSize);

        ALuint buffer;
        alCall(alGenBuffers, 1, &buffer);

        ALenum format;
        if (channels == 1 && bitsPerSample == 8)
            format = AL_FORMAT_MONO8;
        else if (channels == 1 && bitsPerSample == 16)
            format = AL_FORMAT_MONO16;
        else if (channels == 2 && bitsPerSample == 8)
            format = AL_FORMAT_STEREO8;
        else if (channels == 2 && bitsPerSample == 16)
            format = AL_FORMAT_STEREO16;
        else
        {
            std::cerr
                << "ERROR: unrecognised wave format: "
                << channels << " channels, "
                << bitsPerSample << " bps" << std::endl;
            return 0;
        }

        alCall(alBufferData, buffer, format, soundData.data(), soundData.size(), sampleRate);
        soundData.clear(); // erase the sound in RAM

        ALuint source;
        alCall(alGenSources, 1, &source);
        alCall(alSourcef, source, AL_PITCH, 1);
        alCall(alSourcef, source, AL_GAIN, 1.0f);
        alCall(alSource3f, source, AL_POSITION, 0, 0, 0);
        alCall(alSource3f, source, AL_VELOCITY, 0, 0, 0);
        alCall(alSourcei, source, AL_LOOPING, AL_FALSE);
        alCall(alSourcei, source, AL_BUFFER, buffer);
 
All of the above code would be in your load_sound function. A good idea would be to have the OpenAL Source (the ALuint value) be returned from your load_sound function. That way you can use it as a handle to play back later; in other words, loading the sound doesn't play it, it just prepares it for playing every time the space-bar is pressed.

You'll then need a play_sound function which takes the OpenAL Source value from load_sound and plays it; this is what you'd call every time space-bar is pressed.

Code: Select all

        alCall(alSourcePlay, source);

        /*ALint state = AL_PLAYING;

        while (state == AL_PLAYING)
        {
            alCall(alGetSourcei, source, AL_SOURCE_STATE, &state);
        }*/
 

Notice that I've commented some of that out. You don't need to check the status of the sound if you just want it to play, this while loop was simply so that the tutorial program didn't exit before the sound has finished playing.

Finally, the following code should only occur once in your program, when it's exiting.

Code: Select all

        alCall(alDeleteSources, 1, &source);
        alCall(alDeleteBuffers, 1, &buffer);

        alcCall(alcMakeContextCurrent, contextMadeCurrent, openALDevice, nullptr);
        alcCall(alcDestroyContext, openALDevice, openALContext);

        ALCboolean closed;
        alcCall(alcCloseDevice, closed, openALDevice, openALDevice);
 
Hope that helps.
Developer of The Last Boundary and webmaster of IndieGameDev.net
Post Reply