Files
CnC_Remastered_Collection/code/REDALERT/AUDMIX.CPP
T
2020-06-14 15:03:29 +02:00

397 lines
11 KiB
C++

/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you 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. For more
* information, see COPYING.
*/
#include "function.h"
#include "AUDIOMIX.H"
#include <AL/al.h>
#include <AL/alc.h>
ALCdevice* device = nullptr;
ALCcontext* context = nullptr;
struct AudioBuffer_t {
ALuint buffer;
ALsizei size;
ALsizei frequency;
ALenum format;
};
enum AudioBufferType_t {
AUDIO_BUFFER_HOUSE_NONE = 0,
AUDIO_BUFFER_HOUSE_ALIED,
AUDIO_BUFFER_HOUSE_SOVIET,
AUDIO_BUFFER_NUMTYPES
};
#define MAX_PRECACHE_AUDIO 512
#define MAX_STREAM_BUFFERS 20
AudioBuffer_t precacheAudioTable[AUDIO_BUFFER_NUMTYPES][MAX_PRECACHE_AUDIO];
AudioBuffer_t speechPrecacheAudioTable[MAX_PRECACHE_AUDIO];
AudioBuffer_t musicPrecacheAudioTable[MAX_PRECACHE_AUDIO];
ALuint streaming_buffers[MAX_STREAM_BUFFERS];
int currentMusicPlaying = -1;
int currentAudioTSource = 0;
struct RIFF_Header {
char chunkID[4];
long chunkSize;
char format[4];
};
struct WAVE_Format {
char subChunkID[4];
long subChunkSize;
short audioFormat;
short numChannels;
long sampleRate;
long byteRate;
short blockAlign;
short bitsPerSample;
};
struct WAVE_Data {
char subChunkID[4];
long subChunk2Size;
};
#define MAX_AUDIO_SOURCES 32
#define SPEECH_AUDIO_SOURCE (MAX_AUDIO_SOURCES - 3)
#define MOVIE_AUDIO_SOURCE (MAX_AUDIO_SOURCES - 2)
#define MUSIC_AUDIO_SOURCE (MAX_AUDIO_SOURCES - 1)
ALuint audio_sources[MAX_AUDIO_SOURCES];
int current_streaming_audiosource = 0;
bool loadWavFile(const char* filename, AudioBuffer_t &audioBuffer) {
FILE* soundFile = NULL;
WAVE_Format wave_format;
RIFF_Header riff_header;
WAVE_Data wave_data;
unsigned char* data;
ALuint* buffer = &audioBuffer.buffer;
ALsizei* size = &audioBuffer.size;
ALsizei* frequency = &audioBuffer.frequency;
ALenum* format = &audioBuffer.format;
try {
soundFile = fopen(filename, "rb");
if (!soundFile) {
return false;
}
fread(&riff_header, sizeof(RIFF_Header), 1, soundFile);
if ((riff_header.chunkID[0] != 'R' ||
riff_header.chunkID[1] != 'I' ||
riff_header.chunkID[2] != 'F' ||
riff_header.chunkID[3] != 'F') &&
(riff_header.format[0] != 'W' ||
riff_header.format[1] != 'A' ||
riff_header.format[2] != 'V' ||
riff_header.format[3] != 'E'))
throw ("Invalid RIFF or WAVE Header");
fread(&wave_format, sizeof(WAVE_Format), 1, soundFile);
if (wave_format.subChunkID[0] != 'f' ||
wave_format.subChunkID[1] != 'm' ||
wave_format.subChunkID[2] != 't' ||
wave_format.subChunkID[3] != ' ')
throw ("Invalid Wave Format");
if (wave_format.subChunkSize > 16)
fseek(soundFile, sizeof(short), SEEK_CUR);
fread(&wave_data, sizeof(WAVE_Data), 1, soundFile);
if (wave_data.subChunkID[0] != 'd' ||
wave_data.subChunkID[1] != 'a' ||
wave_data.subChunkID[2] != 't' ||
wave_data.subChunkID[3] != 'a')
throw ("Invalid data header");
data = new unsigned char[wave_data.subChunk2Size];
if (!fread(data, wave_data.subChunk2Size, 1, soundFile))
throw ("error loading WAVE data into struct!");
*size = wave_data.subChunk2Size;
*frequency = wave_format.sampleRate;
if (wave_format.numChannels == 1) {
if (wave_format.bitsPerSample == 8)
*format = AL_FORMAT_MONO8;
else if (wave_format.bitsPerSample == 16)
*format = AL_FORMAT_MONO16;
}
else if (wave_format.numChannels == 2) {
if (wave_format.bitsPerSample == 8)
*format = AL_FORMAT_STEREO8;
else if (wave_format.bitsPerSample == 16)
*format = AL_FORMAT_STEREO16;
}
alGenBuffers(1, buffer);
alBufferData(*buffer, *format, (void*)data,
*size, *frequency);
fclose(soundFile);
return true;
}
catch (char* error) {
if (soundFile != NULL)
fclose(soundFile);
return false;
}
}
/*
=============
AudMix_IsEnabled
=============
*/
bool AudMix_IsEnabled(void) {
return device != NULL && context != NULL;
}
/*
=============
AudMix_PlayMusic
=============
*/
void AudMix_PlayMusic(int musicId) {
if (currentMusicPlaying != -1) {
//alSourcei(musicPrecacheAudioTable[currentMusicPlaying].sourceId, AL_LOOPING, 0);
alSourceStop(audio_sources[MAX_AUDIO_SOURCES - 1]);
}
alSourcei(audio_sources[MAX_AUDIO_SOURCES - 1], AL_BUFFER, musicPrecacheAudioTable[musicId].buffer);
alSourcei(audio_sources[MAX_AUDIO_SOURCES - 1], AL_LOOPING, 1);
alSourcePlay(audio_sources[MAX_AUDIO_SOURCES - 1]);
currentMusicPlaying = musicId;
}
bool AudMix_IsSourcePlaying(ALuint source)
{
ALenum state;
alGetSourcei(source, AL_SOURCE_STATE, &state);
return (state == AL_PLAYING);
}
void AudMix_TickMovieAudio(void) {
int numBuffers = 0;
alGetSourcei(audio_sources[MOVIE_AUDIO_SOURCE], AL_BUFFERS_PROCESSED, &numBuffers);
while (numBuffers--)
{
ALuint buffer;
alSourceUnqueueBuffers(audio_sources[MOVIE_AUDIO_SOURCE], 1, &buffer);
}
}
/*
=============
AudMix_QueueVideoAudio
=============
*/
void AudMix_QueueVideoAudio(const char* audio_buffer, int length, int rate) {
int numBuffers = 0;
alGetSourcei(audio_sources[MOVIE_AUDIO_SOURCE], AL_BUFFERS_PROCESSED, &numBuffers);
while (numBuffers--)
{
ALuint buffer;
alSourceUnqueueBuffers(audio_sources[MOVIE_AUDIO_SOURCE], 1, &buffer);
}
numBuffers = 0;
alGetSourcei(audio_sources[MOVIE_AUDIO_SOURCE], AL_BUFFERS_QUEUED, &numBuffers);
if (numBuffers == MAX_STREAM_BUFFERS)
{
// assert(!"Steam dropping raw samples, reached MAX_STREAM_BUFFERS");
return;
}
if (current_streaming_audiosource >= MAX_STREAM_BUFFERS)
current_streaming_audiosource = 0;
alBufferData(streaming_buffers[current_streaming_audiosource], AL_FORMAT_MONO16, (ALvoid*)audio_buffer, length, 22050);
alSourceQueueBuffers(audio_sources[MOVIE_AUDIO_SOURCE], 1, &streaming_buffers[current_streaming_audiosource]);
if(!AudMix_IsSourcePlaying(audio_sources[MOVIE_AUDIO_SOURCE]))
alSourcePlay(audio_sources[MOVIE_AUDIO_SOURCE]);
//MOVIE_AUDIO_SOURCE
current_streaming_audiosource++;
}
/*
=============
AudMix_Init
=============
*/
void AudMix_Init(void) {
// Open up the audio device.
device = alcOpenDevice(NULL);
if (!device) {
return;
}
context = alcCreateContext(device, NULL);
if (!alcMakeContextCurrent(context)) {
return;
}
// Precache all of the audio.
for (int i = 0; i < VOC_COUNT; i++) {
char expandedFilePath[512];
sprintf(expandedFilePath, "sound/%s.wav", GetEffectFileName(i));
if (!loadWavFile(expandedFilePath, precacheAudioTable[AUDIO_BUFFER_HOUSE_NONE][i]))
{
sprintf(expandedFilePath, "sound/alied/%s.wav", GetEffectFileName(i));
if (!loadWavFile(expandedFilePath, precacheAudioTable[AUDIO_BUFFER_HOUSE_ALIED][i])) {
// assert(!"failed to load audio!");
}
sprintf(expandedFilePath, "sound/russian/%s.wav", GetEffectFileName(i));
if (!loadWavFile(expandedFilePath, precacheAudioTable[AUDIO_BUFFER_HOUSE_SOVIET][i])) {
//assert(!"failed to load audio!");
}
}
}
// Precache all the Speech
for (int i = 0; i < VOX_COUNT; i++) {
char expandedFilePath[512];
sprintf(expandedFilePath, "sound/speech/%s.wav", GetSpeechFileName(i));
if (!loadWavFile(expandedFilePath, speechPrecacheAudioTable[i])) {
//assert(!"failed to load music!");
}
}
// Precache the music.
for (int i = 0; i < THEME_COUNT; i++) {
char expandedFilePath[512];
sprintf(expandedFilePath, "sound/music/%s.wav", GetThemeMusicFileName(i));
if (!loadWavFile(expandedFilePath, musicPrecacheAudioTable[i])) {
//assert(!"failed to load music!");
}
}
// Allocate the audio sources.
alGenSources(MAX_AUDIO_SOURCES, &audio_sources[0]);
for(int i = 0; i < MAX_AUDIO_SOURCES; i++)
alSource3f(audio_sources[i], AL_POSITION, 0.0f, 0.0f, 0.0f);
alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
alGenBuffers(MAX_STREAM_BUFFERS, streaming_buffers);
}
void PlayAudio(AudioBuffer_t& buffer, bool speech) {
// Only one speech sound can play at once.
if (speech) {
//if (AudMix_IsSourcePlaying(SPEECH_AUDIO_SOURCE))
// return;
alSourceStop(audio_sources[SPEECH_AUDIO_SOURCE]);
alSourcei(audio_sources[SPEECH_AUDIO_SOURCE], AL_BUFFER, buffer.buffer);
alSourcePlay(audio_sources[SPEECH_AUDIO_SOURCE]);
return;
}
if (currentAudioTSource >= MAX_AUDIO_SOURCES - 3)
currentAudioTSource = 0;
alSourcei(audio_sources[currentAudioTSource], AL_BUFFER, buffer.buffer);
alSourcePlay(audio_sources[currentAudioTSource]);
currentAudioTSource++;
}
void AudMix_SetMusicState(bool musicState) {
if (!musicState) {
alSourcePause(audio_sources[MUSIC_AUDIO_SOURCE]);
}
else {
alSourcePlay(audio_sources[MUSIC_AUDIO_SOURCE]);
}
}
void On_Sound_Effect(int sound_index, int variation, COORDINATE coord, int house)
{
// MBL 06.17.2019
int voc = sound_index;
if (voc == VOC_NONE)
{
return;
}
if (precacheAudioTable[AUDIO_BUFFER_HOUSE_NONE][sound_index].buffer != 0) {
PlayAudio(precacheAudioTable[AUDIO_BUFFER_HOUSE_NONE][sound_index], false);
}
else if(PlayerPtr != NULL) {
// We have to play a faction audio.
if (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE || PlayerPtr->Class->House == HOUSE_TURKEY) {
PlayAudio(precacheAudioTable[AUDIO_BUFFER_HOUSE_SOVIET][sound_index], false);
}
else {
PlayAudio(precacheAudioTable[AUDIO_BUFFER_HOUSE_ALIED][sound_index], false);
}
}
}
// void On_Speech(int speech_index) // MBL 02.06.2020
void On_Speech(int speech_index, HouseClass* house)
{
if (house == NULL) {
PlayAudio(speechPrecacheAudioTable[speech_index], true);
}
// DLLExportClass::On_Speech(PlayerPtr, speech_index); // MBL 02.06.2020
//if (house == NULL) {
// DLLExportClass::On_Speech(PlayerPtr, speech_index);
//}
//else
//{
// DLLExportClass::On_Speech(house, speech_index);
//}
}
/***********************************************************************************************
* Is_Speaking -- Checks to see if the eva voice is still playing. *
* *
* Call this routine when the EVA voice being played needs to be checked. A typical use *
* of this would be when some action needs to be delayed until the voice has finished -- *
* say the end of the game. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Is the EVA voice still playing? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/12/1995 JLB : Created. *
*=============================================================================================*/
bool Is_Speaking(void)
{
Speak_AI();
return AudMix_IsSourcePlaying(SPEECH_AUDIO_SOURCE);
}