// Sound management
//
// Copyright (C) 2003-2019 Sam Varner
//
// This file is part of Vamos Automotive Simulator.
//
// Vamos is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vamos is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vamos. If not, see <http://www.gnu.org/licenses/>.
#include "Sounds.hpp"
#include "../geometry/Numeric.hpp"
#include <AL/alut.h>
#include <cassert>
#include <sstream>
using namespace Vamos_Geometry;
using namespace Vamos_Media;
using namespace Vamos_World;
// Define an OpenAL exit handler so resources will be freed when the sound
// object is destroyed.
void exit_alut()
{
alutExit();
}
Sounds::Sounds(double volume)
{
alutInit(0, 0);
alDistanceModel(AL_INVERSE_DISTANCE);
set_volume(volume);
// Register the exit handler.
atexit(exit_alut);
}
void Sounds::set_volume(double volume)
{
alListenerf(AL_GAIN, volume);
}
void Sounds::add_sample(std::string file, Sound_Type type, double volume, double pitch)
{
file = m_data_dir + file;
switch (type)
{
case TIRE_SQUEAL:
mp_tire_squeal_sound.reset(new Sample(file, volume, pitch, true));
break;
case KERB:
mp_kerb_sound.reset(new Sample(file, volume, pitch, true));
break;
case GRASS:
mp_grass_sound.reset(new Sample(file, volume, pitch, true));
break;
case GRAVEL:
mp_gravel_sound.reset(new Sample(file, volume, pitch, true));
break;
case SCRAPE:
mp_scrape_sound.reset(new Sample(file, volume, pitch, true));
break;
case WIND:
mp_wind_sound.reset(new Sample(file, volume, pitch, true));
break;
case SOFT_CRASH:
mp_soft_crash_sound.reset(new Sample(file, volume, pitch, false));
break;
case HARD_CRASH:
mp_hard_crash_sound.reset(new Sample(file, volume, pitch, false));
break;
default:
assert(false);
}
}
void Sounds::read(std::string data_dir, std::string sounds_file)
{
mp_soft_crash_sound.reset();
mp_hard_crash_sound.reset();
mp_wind_sound.reset();
mp_scrape_sound.reset();
mp_gravel_sound.reset();
mp_grass_sound.reset();
mp_kerb_sound.reset();
mp_tire_squeal_sound.reset();
if (!data_dir.empty())
m_data_dir = data_dir;
if (!sounds_file.empty())
m_sounds_file = sounds_file;
Sounds_Reader reader(m_data_dir + m_sounds_file, this);
}
void Sounds::play_tire_squeal_sound(double slide, const Three_Vector& position)
{
if (!mp_tire_squeal_sound)
return;
if (slide < 0.5)
{
mp_tire_squeal_sound->pause();
return;
}
const double pitch = clip(2.0 * (1.0 - slide), 0.8, 4.0);
mp_tire_squeal_sound->pitch(pitch);
mp_tire_squeal_sound->volume(0.1 * slide);
mp_tire_squeal_sound->position(position);
mp_grass_sound->pause();
mp_gravel_sound->pause();
mp_scrape_sound->pause();
mp_tire_squeal_sound->play();
}
void Sounds::play_kerb_sound(double speed, const Three_Vector& position)
{
if (speed <= 0.0)
{
mp_kerb_sound->pause();
return;
}
mp_kerb_sound->volume(1.0);
mp_kerb_sound->pitch(speed);
mp_kerb_sound->position(position);
mp_kerb_sound->play();
}
void Sounds::play_grass_sound(double speed, const Three_Vector& position)
{
if (speed <= 0.0)
{
mp_grass_sound->pause();
return;
}
mp_grass_sound->volume(clip(0.05 * speed, 0.0, 1.0));
mp_grass_sound->position(position);
mp_grass_sound->play();
mp_gravel_sound->pause();
}
void Sounds::play_gravel_sound(double speed, const Three_Vector& position)
{
if (speed <= 0.0)
{
mp_gravel_sound->pause();
return;
}
mp_gravel_sound->volume(clip(0.08 * speed, 0.0, 1.0));
mp_gravel_sound->position(position);
mp_gravel_sound->play();
mp_grass_sound->pause();
}
void Sounds::play_scrape_sound(double speed, const Three_Vector& position)
{
if (speed <= 0.0)
{
mp_scrape_sound->pause();
return;
}
mp_scrape_sound->volume(clip(0.1 * speed, 0.0, 1.0));
mp_scrape_sound->position(position);
mp_tire_squeal_sound->pause();
mp_grass_sound->pause();
mp_gravel_sound->pause();
mp_scrape_sound->play();
}
void Sounds::play_wind_sound(double speed, const Three_Vector& position)
{
if (speed <= 0.0)
{
mp_wind_sound->pause();
return;
}
mp_wind_sound->volume(clip(0.005 * speed, 0.0, 1.0));
mp_wind_sound->position(position);
mp_wind_sound->play();
}
void Sounds::play_hard_crash_sound(double speed, const Three_Vector& position)
{
double volume = clip(0.1 * (speed - 1.0), 0.0, 1.0);
if (volume == 0.0)
return;
mp_hard_crash_sound->volume(volume);
mp_hard_crash_sound->position(position);
mp_hard_crash_sound->play();
}
void Sounds::play_soft_crash_sound(double speed, const Three_Vector& position)
{
double volume = clip(0.1 * (speed - 1.0), 0.0, 1.0);
if (volume == 0.0)
return;
mp_soft_crash_sound->volume(volume);
mp_soft_crash_sound->position(position);
mp_soft_crash_sound->play();
}
void Sounds::pause()
{
//! Might be good to have a registration system to make it easier
//! to do something to all sounds.
if (mp_tire_squeal_sound)
mp_tire_squeal_sound->pause();
if (mp_kerb_sound)
mp_kerb_sound->pause();
if (mp_grass_sound)
mp_grass_sound->pause();
if (mp_gravel_sound)
mp_gravel_sound->pause();
if (mp_scrape_sound)
mp_scrape_sound->pause();
if (mp_wind_sound)
mp_wind_sound->pause();
if (mp_soft_crash_sound)
mp_soft_crash_sound->pause();
if (mp_hard_crash_sound)
mp_hard_crash_sound->pause();
}
//----------------------------------------------------------------------------------------
Sounds_Reader::Sounds_Reader(std::string file_name, Sounds* sounds)
: mp_sounds(sounds),
m_rate(8000), // The default sample rate is 8 kHz.
m_buffer_duration(0.2)
{
read(file_name);
}
void Sounds_Reader::on_end_tag(const XML_Tag& tag)
{
Sounds::Sound_Type type = Sounds::NONE;
if (label() == "tire-squeal")
type = Sounds::TIRE_SQUEAL;
else if (label() == "kerb-sound")
type = Sounds::KERB;
else if (label() == "grass-sound")
type = Sounds::GRASS;
else if (label() == "gravel-sound")
type = Sounds::GRAVEL;
else if (label() == "scrape-sound")
type = Sounds::SCRAPE;
else if (label() == "wind-sound")
type = Sounds::WIND;
else if (label() == "soft-crash-sound")
type = Sounds::SOFT_CRASH;
else if (label() == "hard-crash-sound")
type = Sounds::HARD_CRASH;
if (type != Sounds::NONE)
mp_sounds->add_sample(m_file, type, m_volume, m_pitch);
}
void Sounds_Reader::on_data(std::string data)
{
if (data.empty())
return;
std::istringstream is(data.c_str());
if (label() == "file")
m_file = data;
else if (label() == "pitch")
is >> m_pitch;
else if (label() == "volume")
is >> m_volume;
else if (label() == "sample-rate")
is >> m_rate;
else if (label() == "buffer-duration")
is >> m_buffer_duration;
}