/******************************************************************************
This source file is part of Viewizard Game Engine
For the latest info, see http://www.viewizard.com/
File name: SoundManager.cpp
Copyright (c) 2006-2007 Michael Kurinnoy, Viewizard
All Rights Reserved.
File Version: 3.0
******************************************************************************
AstroMenace game source code available under "dual licensing" model.
The licensing options available are:
* Commercial Licensing. This is the appropriate option if you are
creating proprietary applications and you are not prepared to
distribute and share the source code of your application.
Contact us for pricing at viewizard@viewizard.com
* Open Source Licensing. This is the appropriate option if you want
to share the source code of your application with everyone you
distribute it to, and you also want to give them the right to share
who uses it. You should have received a copy of the GNU General Public
License version 3 with this source codes.
If not, see .
******************************************************************************/
#include "Sound.h"
#include "Buffer.h"
#include "../System/System.h"
//------------------------------------------------------------------------------------
// переменные
//------------------------------------------------------------------------------------
eSound *StartSoundMan = 0;
eSound *EndSoundMan = 0;
int NumSoundMan = 0;
eMusic *StartMusicMan = 0;
eMusic *EndMusicMan = 0;
int NumMusicMan = 0;
//------------------------------------------------------------------------------------
// Получение ошибок
//------------------------------------------------------------------------------------
ALboolean CheckALCError(ALCdevice *Device)
{
ALenum ErrCode;
char Err[1024];
strcpy(Err, "ALC error: ");
if ((ErrCode = alcGetError(Device)) != ALC_NO_ERROR)
{
strcat(Err, alcGetString(Device, ErrCode));
strcat(Err, "\n");
fprintf(stderr, Err);
return AL_FALSE;
}
return AL_TRUE;
}
ALboolean CheckALError()
{
ALenum ErrCode;
char Err[1024];
strcpy(Err, "OpenAL error: ");
if ((ErrCode = alGetError()) != AL_NO_ERROR)
{
strcat(Err, alGetString(ErrCode));
strcat(Err, "\n");
fprintf(stderr, Err);
return AL_FALSE;
}
return AL_TRUE;
}
ALboolean CheckALUTError()
{
ALenum ErrCode;
char Err[1024];
strcpy(Err, "OpenAL alut error: ");
if ((ErrCode = alutGetError()) != ALUT_ERROR_NO_ERROR)
{
strcat(Err, alutGetErrorString(ErrCode));
strcat(Err, "\n");
fprintf(stderr, Err);
return AL_FALSE;
}
return AL_TRUE;
}
//------------------------------------------------------------------------------------
// Инициализация звука
//------------------------------------------------------------------------------------
bool vw_InitSound()
{
// Position of the Listener.
ALfloat ListenerPos[] = { 0.0f, 0.0f, 0.0f };
// Velocity of the Listener.
ALfloat ListenerVel[] = { 0.0f, 0.0f, 0.0f };
// Orientation of the Listener. (first 3 elements are "at", second 3 are "up")
// Also note that these should be units of '1'.
ALfloat ListenerOri[] = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f };
alutInitWithoutContext(NULL, NULL);
if (!CheckALUTError()) return false;
ALCcontext *Context = 0;
ALCdevice *Device = 0;
// Open default sound device
Device = alcOpenDevice(NULL); // "Generic Software"
// Check for errors
if (!Device)
{
fprintf(stderr, "Default sound device not present!\n");
return false;
}
// Creating rendering context
Context = alcCreateContext(Device, NULL);
if (!CheckALCError(Device)) return false;
alcMakeContextCurrent(Context);
if (!CheckALCError(Device)) return false;
// сброс ошибок
alGetError();
alcGetError(Device);
// Set listener properties
alListenerfv(AL_POSITION, ListenerPos);
alListenerfv(AL_VELOCITY, ListenerVel);
alListenerfv(AL_ORIENTATION, ListenerOri);
// затухание звука
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
printf("Vendor : %s\n", alGetString(AL_VENDOR));
printf("Renderer : %s\n", alGetString(AL_RENDERER));
printf("Version : %s\n", alGetString(AL_VERSION));
printf("ALut ver : %i.%i\n", alutGetMajorVersion(), alutGetMinorVersion());
// получаем и выводим все поддерживаемые расширения
char *extensions_tmp;
int i;
size_t len;
extensions_tmp = (char *)alGetString(AL_EXTENSIONS);
if (extensions_tmp != 0)
{
char *extensions = 0;
len = strlen(extensions_tmp);
extensions = new char[len+1];
if (extensions != 0)
{
strcpy(extensions, extensions_tmp);
for (i=0; iSource))
{
// если останавливать играющий звук, возможен щелчек (и в линуксе и в виндовсе)
alSourceStop(Sound->Source);
alGetError(); // сброс ошибок
// освобождаем собственно сам источник
alDeleteSources(1, &Sound->Source);
Sound->Source = 0;
alGetError(); // сброс ошибок
}
// освобождаем память
if (Sound != 0){delete Sound; Sound = 0;}
}
//------------------------------------------------------------------------------------
// освобождаем все подключенные к менеджеру
//------------------------------------------------------------------------------------
void vw_ReleaseAllSounds(int ReleaseType)
{
if (ReleaseType == 0)
{
// Чистка памяти...
eSound *Tmp = StartSoundMan;
while (Tmp != 0)
{
eSound *Tmp1 = Tmp->Next;
vw_ReleaseSound(Tmp);
Tmp = Tmp1;
}
StartSoundMan = 0;
EndSoundMan = 0;
}
else
{
eSound *Tmp = StartSoundMan;
while (Tmp != 0)
{
eSound *Tmp1 = Tmp->Next;
if (Tmp->NeedRelease) vw_ReleaseSound(Tmp);
Tmp = Tmp1;
}
}
}
//------------------------------------------------------------------------------------
// подключение звука к менеджеру
//------------------------------------------------------------------------------------
void vw_AttachSound(eSound* Sound)
{
if (Sound == 0) return;
// первый в списке...
if (EndSoundMan == 0)
{
Sound->Prev = 0;
Sound->Next = 0;
NumSoundMan += 1;
Sound->Num = NumSoundMan;
StartSoundMan = Sound;
EndSoundMan = Sound;
}
else // продолжаем заполнение...
{
Sound->Prev = EndSoundMan;
Sound->Next = 0;
EndSoundMan->Next = Sound;
NumSoundMan += 1;
Sound->Num = NumSoundMan;
EndSoundMan = Sound;
}
}
//------------------------------------------------------------------------------------
// отключение от менеджера
//------------------------------------------------------------------------------------
void vw_DetachSound(eSound* Sound)
{
if (Sound == 0) return;
// переустанавливаем указатели...
if (StartSoundMan == Sound) StartSoundMan = Sound->Next;
if (EndSoundMan == Sound) EndSoundMan = Sound->Prev;
if (Sound->Next != 0) Sound->Next->Prev = Sound->Prev;
else if (Sound->Prev != 0) Sound->Prev->Next = 0;
if (Sound->Prev != 0) Sound->Prev->Next = Sound->Next;
else if (Sound->Next != 0) Sound->Next->Prev = 0;
}
//------------------------------------------------------------------------------------
// Нахождение по уникальному номеру
//------------------------------------------------------------------------------------
eSound* vw_FindSoundByNum(int Num)
{
eSound *Tmp = StartSoundMan;
while (Tmp != 0)
{
eSound *Tmp1 = Tmp->Next;
if (Tmp->Num == Num) return Tmp;
Tmp = Tmp1;
}
return 0;
}
//------------------------------------------------------------------------------------
// Нахождение по имени
//------------------------------------------------------------------------------------
eSound* vw_FindSoundByName(const char *Name)
{
if (Name == 0) return 0;
eSound *Tmp = StartSoundMan;
while (Tmp != 0)
{
eSound *Tmp1 = Tmp->Next;
if (Tmp->FileName != 0)
if (!strcmp(Tmp->FileName, Name)) return Tmp;
Tmp = Tmp1;
}
return 0;
}
//------------------------------------------------------------------------------------
// Обновляем данные в менеджере
//------------------------------------------------------------------------------------
void vw_UpdateSound()
{
eSound *Tmp = StartSoundMan;
float CurrentGetTime = vw_GetTime();
while (Tmp != 0)
{
eSound *Tmp1 = Tmp->Next;
// считаем сколько играем этот эффект
float DeltaTime = CurrentGetTime - Tmp->LastUpdateTime;
Tmp->Age += DeltaTime;
Tmp->LastUpdateTime = CurrentGetTime;
if (alIsSource(Tmp->Source))
if (Tmp->DestroyTimeStart > 0.0f)
{
if (Tmp->DestroyTime > 0.0f)
{
Tmp->DestroyTime -= DeltaTime;
if (Tmp->DestroyTime < 0.0f) Tmp->DestroyTime = 0.0f;
alSourcef (Tmp->Source, AL_GAIN, Tmp->MainVolume*Tmp->Volume*(Tmp->DestroyTime/Tmp->DestroyTimeStart));
alGetError(); // сброс ошибок
}
else
{
// уже нулевая громкость, можем удалять
vw_ReleaseSound(Tmp);
Tmp = 0;
}
}
// смотрим, если уже не играем - надо удалить
if (Tmp != 0)
if (alIsSource(Tmp->Source))
{
ALint TMPS;
alGetSourcei(Tmp->Source, AL_SOURCE_STATE, &TMPS);
alGetError(); // сброс ошибок
if (TMPS == AL_STOPPED) vw_ReleaseSound(Tmp);
}
Tmp = Tmp1;
}
}
//------------------------------------------------------------------------------------
// Установка громкости всем SFX
//------------------------------------------------------------------------------------
void vw_SetSoundMainVolume(float NewMainVolume)
{
eSound *Tmp = StartSoundMan;
while (Tmp != 0)
{
eSound *Tmp1 = Tmp->Next;
Tmp->SetMainVolume(NewMainVolume);
Tmp = Tmp1;
}
}
//------------------------------------------------------------------------------------
// Проверяем, можем ли мы играть звук с данными параметрами
//------------------------------------------------------------------------------------
bool vw_CheckCanPlaySound(int Group, int GroupCount, int SubGroup, int SubGroupCount, int Priority)
{
// находим кол-во звуков в группе
int GroupCurrentCount = 0;
// находим кол-во звуков в подгруппе
int SubGroupCurrentCount = 0;
// находим звук, из этой группы с наименьшим приоритетом + самый старый
eSound *GroupCanStop = 0;
// находим звук, из этой подгруппы с меньшим или равным приоритетом + самый старый
eSound *SubGroupCanStop = 0;
eSound *Tmp = StartSoundMan;
while (Tmp != 0)
{
eSound *Tmp1 = Tmp->Next;
if (Tmp->DestroyTimeStart == -1.0f)
if (Tmp->Group == Group)
{
if (Tmp->SubGroup == SubGroup)
{
SubGroupCurrentCount++;
// если приоритет выше или такой же, можем его останавливать
if (Priority <= Tmp->Priority)
{
if (SubGroupCanStop == 0)
SubGroupCanStop = Tmp;// если ничего еще нет, ставим этот
else
{
if (SubGroupCanStop->Priority < Tmp->Priority)
SubGroupCanStop = Tmp; // если этот с меньшим приоритетом - берем сразу
else
if (SubGroupCanStop->Age < Tmp->Age)
SubGroupCanStop = Tmp; // меняем на более старый
}
}
}
GroupCurrentCount++;
// если приоритет выше или такой же, можем его останавливать
if (Priority <= Tmp->Priority)
{
if (GroupCanStop == 0)
GroupCanStop = Tmp;// если ничего еще нет, ставим этот
else
{
if (GroupCanStop->Priority < Tmp->Priority)
GroupCanStop = Tmp; // если этот с меньшим приоритетом - берем сразу
else
if (GroupCanStop->Age < Tmp->Age)
GroupCanStop = Tmp; // меняем на более старый
}
}
}
Tmp = Tmp1;
}
// если в подгруппе нет места - останавливаем звук из подгруппы
if (SubGroupCount <= SubGroupCurrentCount)
{
if (SubGroupCanStop != 0)
{
SubGroupCanStop->Stop(0.15f);
GroupCurrentCount--;
SubGroupCurrentCount--;
}
}
else // если уже удалили, само собой в группе будет пустое место
{
// если в группе нет места - останавливаем звук из группы
if (GroupCount <= GroupCurrentCount)
if (GroupCanStop != 0)
{
GroupCanStop->Stop(0.15f);
GroupCurrentCount--;
}
}
// проверяем, если есть место в подгруппе и в группе - говорим, что все ок, можно запускать
// иначе - выходим и не запускаем новый звук с этими настройками
if (SubGroupCount > SubGroupCurrentCount && GroupCount > GroupCurrentCount) return true;
// иначе играть не можем
return false;
}
//------------------------------------------------------------------------------------
// Возвращаем, если играется хотя бы одна музыка
//------------------------------------------------------------------------------------
bool vw_GetMusicIsPlaying()
{
eMusic *Tmp = StartMusicMan;
while (Tmp != 0)
{
eMusic *Tmp1 = Tmp->Next;
// смотрим, если играем что-то, передаем...
if (alIsSource(Tmp->Source))
{
ALint TMPS;
alGetSourcei(Tmp->Source, AL_SOURCE_STATE, &TMPS);
alGetError(); // сброс ошибок
if (TMPS == AL_PLAYING) return true;
}
Tmp = Tmp1;
}
return false;
}
//------------------------------------------------------------------------------------
// Освобождение памяти и удаление
//------------------------------------------------------------------------------------
void vw_ReleaseMusic(eMusic* Music)
{
// проверка входящих данных
if (Music == 0) return;
// отключаем от менерджера
vw_DetachMusic(Music);
// обязательно остановить!!!
if (alIsSource(Music->Source))
{
alSourceStop(Music->Source);
// открепляем все буферы от источника
int queued;
alGetSourcei(Music->Source, AL_BUFFERS_QUEUED, &queued);
alGetError(); // сброс ошибок
while(queued--)
{
ALuint buffer;
alSourceUnqueueBuffers(Music->Source, 1, &buffer);
alGetError(); // сброс ошибок
}
// освобождаем собственно сам источник
alDeleteSources(1, &Music->Source);
Music->Source = 0;
alGetError(); // сброс ошибок
}
// удаляем буфер (для музыки именно тут)
alDeleteBuffers(NUM_OF_DYNBUF, Music->Buffers);
alGetError(); // сброс ошибок
if (Music->mVF != 0){ov_clear(Music->mVF);delete Music->mVF; Music->mVF = 0;}
if (Music->MusicFile != 0)
{
eFILE *TMPFile = (eFILE *)Music->MusicFile;
vw_fclose(TMPFile);
}
// освобождаем память
if (Music != 0){delete Music; Music = 0;}
}
//------------------------------------------------------------------------------------
// освобождаем все подключенные к менеджеру
//------------------------------------------------------------------------------------
void vw_ReleaseAllMusic()
{
// Чистка памяти...
eMusic *Tmp = StartMusicMan;
while (Tmp != 0)
{
eMusic *Tmp1 = Tmp->Next;
vw_ReleaseMusic(Tmp);
Tmp = Tmp1;
}
StartMusicMan = 0;
EndMusicMan = 0;
}
//------------------------------------------------------------------------------------
// подключение звука к менеджеру
//------------------------------------------------------------------------------------
void vw_AttachMusic(eMusic* Music)
{
if (Music == 0) return;
// первый в списке...
if (EndMusicMan == 0)
{
Music->Prev = 0;
Music->Next = 0;
NumMusicMan += 1;
Music->Num = NumMusicMan;
StartMusicMan = Music;
EndMusicMan = Music;
}
else // продолжаем заполнение...
{
Music->Prev = EndMusicMan;
Music->Next = 0;
EndMusicMan->Next = Music;
NumMusicMan += 1;
Music->Num = NumMusicMan;
EndMusicMan = Music;
}
}
//------------------------------------------------------------------------------------
// отключение от менеджера
//------------------------------------------------------------------------------------
void vw_DetachMusic(eMusic* Music)
{
if (Music == 0) return;
// переустанавливаем указатели...
if (StartMusicMan == Music) StartMusicMan = Music->Next;
if (EndMusicMan == Music) EndMusicMan = Music->Prev;
if (Music->Next != 0) Music->Next->Prev = Music->Prev;
else if (Music->Prev != 0) Music->Prev->Next = 0;
if (Music->Prev != 0) Music->Prev->Next = Music->Next;
else if (Music->Next != 0) Music->Next->Prev = 0;
}
//------------------------------------------------------------------------------------
// Обновляем данные в менеджере
//------------------------------------------------------------------------------------
void vw_UpdateMusic()
{
eMusic *Tmp = StartMusicMan;
while (Tmp != 0)
{
eMusic *Tmp1 = Tmp->Next;
if (!Tmp->Update()) vw_ReleaseMusic(Tmp);
Tmp = Tmp1;
}
}
//------------------------------------------------------------------------------------
// Установка громкости у всех
//------------------------------------------------------------------------------------
void vw_SetMusicMainVolume(float NewMainVolume)
{
eMusic *Tmp = StartMusicMan;
while (Tmp != 0)
{
eMusic *Tmp1 = Tmp->Next;
Tmp->SetMainVolume(NewMainVolume);
Tmp = Tmp1;
}
}
//------------------------------------------------------------------------------------
// Нахождение по уникальному номеру...
//------------------------------------------------------------------------------------
eMusic* vw_FindMusicByNum(int Num)
{
eMusic *Tmp = StartMusicMan;
while (Tmp != 0)
{
eMusic *Tmp1 = Tmp->Next;
if (Tmp->Num == Num) return Tmp;
Tmp = Tmp1;
}
return 0;
}