extremetuxracer/src/audio.cpp

361 lines
8.9 KiB
C++

/* --------------------------------------------------------------------
EXTREME TUXRACER
Copyright (C) 2010 Extreme Tuxracer Team
This program 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 2
of the License, or (at your option) any later version.
This program 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.
---------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#include <etr_config.h>
#endif
#include "audio.h"
#include "spx.h"
#include <SDL/SDL.h>
// the global instances of the 3 audio classes
CAudio Audio;
CMusic Music;
CSound Sound;
// --------------------------------------------------------------------
// class CAudio
// --------------------------------------------------------------------
CAudio::CAudio() {
IsOpen = false;
}
void CAudio::Open() {
// first initialize audio (SDL, not SDL_Mixer).
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
Message("Couldn't initialize SDL Audio", SDL_GetError());
return;
}
Uint16 format = AUDIO_S16SYS;
int channels = 2;
if (Mix_OpenAudio(param.audio_freq, format, channels, param.audio_buffer_size) < 0)
Message("Couldn't open SDL_mixer", Mix_GetError());
IsOpen = CheckOpen();
Mix_AllocateChannels(8);
}
void CAudio::Close() {
if (IsOpen) {
Music.FreeMusics();
Sound.FreeSounds();
Mix_CloseAudio();
IsOpen = false;
}
}
bool CAudio::CheckOpen() {
int freq;
Uint16 format;
int channels;
int ret = Mix_QuerySpec(&freq, &format, &channels);
return (ret > 0);
}
// --------------------------------------------------------------------
// class CSound
// --------------------------------------------------------------------
bool CSound::LoadChunk(const std::string& name, const char *filename) {
if (Audio.IsOpen == false) return false;
sounds.push_back(TSound());
sounds.back().chunk = Mix_LoadWAV(filename);
if (sounds.back().chunk == NULL) return false;
sounds.back().channel = -1; // default: no channel
sounds.back().loop_count = 0; // default: playing once
Mix_VolumeChunk(sounds.back().chunk, param.sound_volume);
SoundIndex[name] = sounds.size()-1;
return true;
}
// Load all soundfiles listed in "/sounds/sounds.lst"
void CSound::LoadSoundList() {
if (!Audio.IsOpen) {
Message("cannot load music, first open Audio");
return;
}
CSPList list(200);
if (list.Load(param.sounds_dir, "sounds.lst")) {
for (size_t i=0; i<list.Count(); i++) {
const string& line = list.Line(i);
string name = SPStrN(line, "name");
string soundfile = SPStrN(line, "file");
string path = MakePathStr(param.sounds_dir, soundfile);
LoadChunk(name, path.c_str());
}
}
}
void CSound::FreeSounds() {
HaltAll();
for (size_t i=0; i<sounds.size(); i++)
if (sounds[i].chunk != NULL)
Mix_FreeChunk(sounds[i].chunk);
sounds.clear();
SoundIndex.clear();
}
size_t CSound::GetSoundIdx(const string& name) const {
if (Audio.IsOpen == false) return -1;
try {
return SoundIndex.at(name);
} catch (...) {
return -1;
}
}
void CSound::SetVolume(size_t soundid, int volume) {
if (Audio.IsOpen == false) return;
if (soundid >= sounds.size()) return;
volume = clamp(0, volume, MIX_MAX_VOLUME);
if (sounds[soundid].chunk == NULL) return;
Mix_VolumeChunk(sounds[soundid].chunk, volume);
}
void CSound::SetVolume(const string& name, int volume) {
SetVolume(GetSoundIdx(name), volume);
}
// ------------------- play -------------------------------------------
void TSound::Play(int loop) {
if (active == true) return;
if (chunk == NULL) return;
channel = Mix_PlayChannel(-1, chunk, loop);
loop_count = loop;
if (loop < 0) active = true;
}
void CSound::Play(size_t soundid, int loop) {
if (!Audio.IsOpen) return;
if (soundid >= sounds.size()) return;
sounds[soundid].Play(loop);
}
void CSound::Play(const string& name, int loop) {
Play(GetSoundIdx(name), loop);
}
void CSound::Play(size_t soundid, int loop, int volume) {
if (!Audio.IsOpen) return;
if (soundid >= sounds.size()) return;
volume = clamp(0, volume, MIX_MAX_VOLUME);
Mix_VolumeChunk(sounds[soundid].chunk, volume);
sounds[soundid].Play(loop);
}
void CSound::Play(const string& name, int loop, int volume) {
Play(GetSoundIdx(name), loop, volume);
}
void CSound::Halt(size_t soundid) {
if (!Audio.IsOpen) return;
if (soundid >= sounds.size()) return;
if (sounds[soundid].chunk == NULL) return;
// loop_count must be -1 (endless loop) for halt
if (sounds[soundid].loop_count < 0) {
Mix_HaltChannel(sounds[soundid].channel);
sounds[soundid].loop_count = 0;
sounds[soundid].channel = -1;
sounds[soundid].active = false;
}
}
void CSound::Halt(const string& name) {
Halt(GetSoundIdx(name));
}
void CSound::HaltAll() {
if (!Audio.IsOpen) return;
Mix_HaltChannel(-1);
for (size_t i=0; i<sounds.size(); i++) {
sounds[i].loop_count = 0;
sounds[i].channel = -1;
sounds[i].active = false;
}
}
// --------------------------------------------------------------------
// class CMusic
// --------------------------------------------------------------------
void Hook() {
Mix_HaltMusic();
PrintStr("halted");
}
CMusic::CMusic() {
curr_music = 0;
curr_volume = 10;
loop_count = 0;
// Mix_HookMusicFinished (Hook);
}
bool CMusic::LoadPiece(const string& name, const char *filename) {
if (!Audio.IsOpen) return -1;
Mix_Music* m = Mix_LoadMUS(filename);
if (m == NULL) {
Message("could not load music", filename);
return false;
}
MusicIndex[name] = musics.size();
musics.push_back(m);
return true;
}
void CMusic::LoadMusicList() {
if (!Audio.IsOpen) {
Message("cannot load music, first open audio");
return;
}
// --- music ---
CSPList list(200);
if (list.Load(param.music_dir, "music.lst")) {
for (size_t i=0; i<list.Count(); i++) {
const string& line = list.Line(i);
string name = SPStrN(line, "name");
string musicfile = SPStrN(line, "file");
string path = MakePathStr(param.music_dir, musicfile);
LoadPiece(name, path.c_str());
}
} else {
Message("could not load music.lst");
return;
}
// --- racing themes ---
list.Clear();
ThemesIndex.clear();
if (list.Load(param.music_dir, "racing_themes.lst")) {
themes.resize(list.Count());
for (size_t i=0; i<list.Count(); i++) {
const string& line = list.Line(i);
string name = SPStrN(line, "name");
ThemesIndex[name] = i;
string item = SPStrN(line, "race", "race_1");
themes[i].situation[0] = musics[MusicIndex[item]];
item = SPStrN(line, "wonrace", "wonrace_1");
themes[i].situation[1] = musics[MusicIndex[item]];
item = SPStrN(line, "lostrace", "lostrace_1");
themes[i].situation[2] = musics[MusicIndex[item]];
}
} else Message("could not load racing_themes.lst");
}
void CMusic::FreeMusics() {
Halt();
for (size_t i=0; i<musics.size(); i++)
if (musics[i] != NULL)
Mix_FreeMusic(musics[i]);
musics.clear();
MusicIndex.clear();
themes.clear();
ThemesIndex.clear();
curr_music = 0;
curr_volume = 10;
}
size_t CMusic::GetMusicIdx(const string& name) const {
if (Audio.IsOpen == false) return -1;
try {
return MusicIndex.at(name);
} catch (...) {
return -1;
}
}
size_t CMusic::GetThemeIdx(const string& theme) const {
if (Audio.IsOpen == false) return -1;
try {
return ThemesIndex.at(theme);
} catch (...) {
return -1;
}
}
void CMusic::SetVolume(int volume) {
int vol = clamp(0, volume, MIX_MAX_VOLUME);
Mix_VolumeMusic(vol);
curr_volume = vol;
}
// If the piece is played in a loop, the volume adjustment gets lost.
// probably a bug in SDL_mixer. Help: we have to refresh the volume
// in each (!) frame.
void CMusic::Update() {
Mix_VolumeMusic(curr_volume);
}
bool CMusic::Play(Mix_Music* music, int loop, int volume) {
if (!music)
return false;
int vol = clamp(0, volume, MIX_MAX_VOLUME);
if (music != curr_music) {
Halt();
Mix_PlayMusic(music, loop);
curr_music = music;
loop_count = loop;
}
Mix_VolumeMusic(vol);
return true;
}
bool CMusic::Play(size_t musid, int loop) {
if (!Audio.IsOpen) return false;
if (musid >= musics.size()) return false;
Mix_Music *music = musics[musid];
return Play(music, loop, curr_volume);
}
bool CMusic::Play(const string& name, int loop) {
return Play(GetMusicIdx(name), loop);
}
bool CMusic::Play(size_t musid, int loop, int volume) {
if (!Audio.IsOpen) return false;
if (musid >= musics.size()) return false;
Mix_Music *music = musics[musid];
return Play(music, loop, volume);
}
bool CMusic::Play(const string& name, int loop, int volume) {
return Play(GetMusicIdx(name), loop, volume);
}
bool CMusic::PlayTheme(size_t theme, ESituation situation) {
if (theme >= themes.size()) return false;
if (situation >= SITUATION_COUNT) return false;
Mix_Music *music = themes [theme].situation[situation];
return Play(music, -1, curr_volume);
}
void CMusic::Halt() {
if (Mix_PlayingMusic()) Mix_HaltMusic();
loop_count = -1;
curr_music = 0;
}