From 4a2bad92171ff8a9a248599f47087cfe39e93653 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Sun, 18 May 2008 09:21:20 +0000 Subject: OpenAL support --- INSTALL | 8 +++- TODO | 3 +- configure.in | 15 ++++-- osirion.kdevelop | 2 +- osirion.kdevelop.pcs | Bin 669199 -> 696918 bytes osirion.kdevses | 15 +++++- src/Makefile.am | 23 +++++---- src/audio/Makefile.am | 6 +++ src/audio/audio.cc | 80 +++++++++++++++++++++++++++++++ src/audio/audio.h | 28 +++++++++++ src/audio/buffers.cc | 112 +++++++++++++++++++++++++++++++++++++++++++ src/audio/buffers.h | 47 ++++++++++++++++++ src/audio/pcm.cc | 49 +++++++++++++++++++ src/audio/pcm.h | 57 ++++++++++++++++++++++ src/audio/wav.cc | 119 ++++++++++++++++++++++++++++++++++++++++++++++ src/audio/wav.h | 22 +++++++++ src/client/client.cc | 39 +++++++-------- src/client/client.h | 25 ++++++++++ src/client/console.cc | 4 +- src/client/console.h | 2 +- src/client/view.cc | 6 +-- src/core/application.cc | 11 +++++ src/core/application.h | 8 ++++ src/core/gameserver.cc | 25 ++++++---- src/core/netconnection.cc | 5 +- src/model/map.cc | 2 +- src/render/image.h | 18 +++---- 27 files changed, 662 insertions(+), 69 deletions(-) create mode 100644 src/audio/Makefile.am create mode 100644 src/audio/audio.cc create mode 100644 src/audio/audio.h create mode 100644 src/audio/buffers.cc create mode 100644 src/audio/buffers.h create mode 100644 src/audio/pcm.cc create mode 100644 src/audio/pcm.h create mode 100644 src/audio/wav.cc create mode 100644 src/audio/wav.h diff --git a/INSTALL b/INSTALL index a459df3..04ac33f 100644 --- a/INSTALL +++ b/INSTALL @@ -8,9 +8,14 @@ The Osirion Project - INSTALL Installation To build the Osirion Project from source code you will need - the SDL and OpenGL library and header files. + the following libraries and their header files: + + SDL version 1.2 or newer + OpenGL, version 1.1 or newer + OpenAL version 1.1 or newer I have succesfully compiled it on the following platforms: + linux-x86_64 gcc 4.1.2 linux-i686 gcc 4.1.2 mingw32 gcc 4.2.2 @@ -167,4 +172,3 @@ Updating cd data/ svn update cd .. - diff --git a/TODO b/TODO index 15cdc5f..1a48e7b 100644 --- a/TODO +++ b/TODO @@ -70,7 +70,8 @@ render: add small camera light, fix lighting without sun sound: - sound would be nice + engine sounds + user interface sounds win32 port: network not functional (ok) diff --git a/configure.in b/configure.in index a855cca..6ea1e8c 100644 --- a/configure.in +++ b/configure.in @@ -125,6 +125,15 @@ GL_LIBS="$HOST_GL_LIBS" AC_SUBST(GL_LIBS) AC_SUBST(GL_CFLAGS) +AC_CHECK_HEADER(AL/al.h, + HAVE_OPENGL=yes + AC_DEFINE(HAVE_OPENAL, 1, [Define this if you have OpenAL]), + HAVE_OPENAL=no +) + +AL_LIBS="-lopenal" +AC_SUBST(AL_LIBS) + AC_MSG_CHECKING([looking for SDL]) KDE_FIND_PATH(sdl-config, LIBSDL_CONFIG, [${prefix}/bin ${exec_prefix}/bin /usr/local/bin /opt/local/bin], [ AC_MSG_WARN([Could not find libSDL, check http://www.sdl.org]) @@ -184,9 +193,9 @@ AC_DEFINE_UNQUOTED(PACKAGE_LIBDIR, "$PACKAGE_LIBDIR", dnl --------------------------------------------------------------- dnl Write makefiles and config.h -AC_OUTPUT(Makefile src/Makefile src/auxiliary/Makefile src/client/Makefile \ - src/core/Makefile src/filesystem/Makefile src/game/Makefile src/math/Makefile \ - src/model/Makefile src/render/Makefile src/server/Makefile src/sys/Makefile) +AC_OUTPUT(Makefile src/Makefile src/audio/Makefile src/auxiliary/Makefile \ + src/client/Makefile src/core/Makefile src/filesystem/Makefile src/game/Makefile \ + src/math/Makefile src/model/Makefile src/render/Makefile src/server/Makefile src/sys/Makefile) dnl --------------------------------------------------------------- dnl Configuration summary diff --git a/osirion.kdevelop b/osirion.kdevelop index 25d8c79..466b3db 100644 --- a/osirion.kdevelop +++ b/osirion.kdevelop @@ -21,7 +21,7 @@ - src/osirion + src/audio/libaudio.la debug diff --git a/osirion.kdevelop.pcs b/osirion.kdevelop.pcs index 6ec8ca8..e3f4194 100644 Binary files a/osirion.kdevelop.pcs and b/osirion.kdevelop.pcs differ diff --git a/osirion.kdevses b/osirion.kdevses index e8415d1..d2d7511 100644 --- a/osirion.kdevses +++ b/osirion.kdevses @@ -1,7 +1,20 @@ - + + + + + + + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 81bd629..6ad49f8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,7 +4,8 @@ SUFFIXES = .rc .rc.o: windres $< -o $@ -SUBDIRS = auxiliary sys math filesystem model core server render client game +SUBDIRS = sys math auxiliary filesystem core model server audio render client \ + game noinst_HEADERS = config.h bin_PROGRAMS = osiriond osirion @@ -24,17 +25,19 @@ osiriond_LDADD = $(top_builddir)/src/auxiliary/libauxiliary.la \ # client osirion_SOURCES = osirion.cc EXTRA_osirion_SOURCES = osirion-res.rc -osirion_DEPENDENCIES = $(ICON_CLIENT) $(top_builddir)/src/client/libclient.la \ +osirion_DEPENDENCIES = $(ICON_CLIENT) \ + $(top_builddir)/src/auxiliary/libauxiliary.la $(top_builddir)/src/client/libclient.la \ $(top_builddir)/src/core/libcore.la $(top_builddir)/src/filesystem/libfilesystem.la \ $(top_builddir)/src/game/libgame.la $(top_builddir)/src/math/libmath.la \ - $(top_builddir)/src/model/libmodel.la $(top_builddir)/src/render/librender.la \ - $(top_builddir)/src/sys/libsys.la $(top_builddir)/src/auxiliary/libauxiliary.la + $(top_builddir)/src/model/libmodel.la $(top_builddir)/src/audio/libaudio.la \ + $(top_builddir)/src/render/librender.la $(top_builddir)/src/sys/libsys.la osirion_CFLAGS = $(LIBSDL_CFLAGS) $(GL_CFLAGS) $(GLUT_CFLAGS) -osirion_LDADD = $(top_builddir)/src/client/libclient.la \ - $(top_builddir)/src/core/libcore.la $(top_builddir)/src/game/libgame.la $(top_builddir)/src/math/libmath.la \ - $(top_builddir)/src/model/libmodel.la $(top_builddir)/src/render/librender.la \ - $(top_builddir)/src/filesystem/libfilesystem.la $(top_builddir)/src/sys/libsys.la \ - $(top_builddir)/src/auxiliary/libauxiliary.la +osirion_LDADD = $(top_builddir)/src/game/libgame.la \ + $(top_builddir)/src/client/libclient.la $(top_builddir)/src/audio/libaudio.la \ + $(top_builddir)/src/render/librender.la $(top_builddir)/src/core/libcore.la \ + $(top_builddir)/src/filesystem/libfilesystem.la $(top_builddir)/src/model/libmodel.la \ + $(top_builddir)/src/math/libmath.la $(top_builddir)/src/auxiliary/libauxiliary.la \ + $(top_builddir)/src/sys/libsys.la $(top_builddir)/src/filesystem/libfilesystem.la $(top_builddir)/src/sys/libsys.la \ $(top_builddir)/src/auxiliary/libauxiliary.la $(GL_LIBS) $(HOST_LIBS) $(ICON_CLIENT) -osirion_LDFLAGS = $(LIBSDL_LIBS) +osirion_LDFLAGS = $(LIBSDL_LIBS) $(GL_LIBS) $(AL_LIBS) diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am new file mode 100644 index 0000000..d1524cf --- /dev/null +++ b/src/audio/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I$(top_srcdir)/src +METASOURCES = AUTO +noinst_LTLIBRARIES = libaudio.la +libaudio_la_LDFLAGS = -avoid-version -no-undefined +noinst_HEADERS = audio.h buffers.h pcm.h wav.h +libaudio_la_SOURCES = audio.cc buffers.cc pcm.cc wav.cc diff --git a/src/audio/audio.cc b/src/audio/audio.cc new file mode 100644 index 0000000..063a079 --- /dev/null +++ b/src/audio/audio.cc @@ -0,0 +1,80 @@ +/* + audio/audio.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "AL/al.h" +#include "AL/alc.h" + +#include "audio/audio.h" +#include "audio/buffers.h" +#include "audio/pcm.h" +#include "sys/sys.h" + +namespace audio +{ + +ALCdevice *audio_device = 0; +ALCcontext *audio_context = 0; + +ALuint source = 0; + +void init() +{ + con_print << "^BInitializing audio..."; + + // open the default audio device + audio_device = alcOpenDevice(0); + + if (!audio_device) { + con_warn << "Could not initialize audio!"; + return; + } + + // create the audio context + audio_context = alcCreateContext(audio_device ,0); + alcMakeContextCurrent(audio_context); + + // clear errors + alGetError(); + + Buffers::init(); + + //con_debug << " device ^B" << alcGetString(audio_device, ALC_DEFAULT_DEVICE_SPECIFIER) << std::endl; + load("ui/chat"); + + // default sound source + alGenSources(1, &source); +} + +void load (const char *name) +{ + Buffers::load(std::string(name)); +} + +void shutdown() +{ + con_print << "^BShutting down audio..."; + + if (audio_context) { + alcMakeContextCurrent(0); + alcDestroyContext(audio_context); + audio_context = 0; + } + + if (audio_device) { + alcCloseDevice(audio_device); + audio_device = 0; + } + + alDeleteSources(1, &source); +} + +void play(const char *name) +{ + Buffers::bind(source, std::string(name)); + alSourcePlay(source); +} + +} diff --git a/src/audio/audio.h b/src/audio/audio.h new file mode 100644 index 0000000..92b8786 --- /dev/null +++ b/src/audio/audio.h @@ -0,0 +1,28 @@ +/* + audio/audio.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_AUDIO_AUDIO_H__ +#define __INCLUDED_AUDIO_AUDIO_H__ + +/// functions to handle audio +namespace audio +{ + +/// initialize the audio subsystem +void init(); + +/// shut down the audio subsystem +void shutdown(); + +/// load an audio sample +void load(const char *name); + +/// play a previously loaded audio sample +void play(const char *name); + +} + +#endif // __INCLUDED_AUDIO_AUDIO_H__ diff --git a/src/audio/buffers.cc b/src/audio/buffers.cc new file mode 100644 index 0000000..9223380 --- /dev/null +++ b/src/audio/buffers.cc @@ -0,0 +1,112 @@ +/* + audio/buffers.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "audio/buffers.h" +#include "audio/pcm.h" +#include "audio/wav.h" +#include "sys/sys.h" + +namespace audio { + +std::map Buffers::registry; +size_t Buffers::index; +ALuint Buffers::buffers[MAXBUFFERS]; + +void Buffers::init() +{ + int error; + clear(); + + alGenBuffers(MAXBUFFERS, buffers); + + if ((error = alGetError()) != AL_NO_ERROR) { + con_warn << "Error " << error << " initializing OpenAL bufers!" << std::endl; + return; + } +} + +void Buffers::shutdown() +{ + alDeleteBuffers(MAXBUFFERS, buffers); + + clear(); +} + +void Buffers::clear() +{ + registry.clear(); + memset(buffers,0, sizeof(buffers)); + index = 0; +} + +size_t Buffers::load(std::string name) +{ + // check if it is already loaded + iterator it = registry.find(name); + if (it != registry.end()) + return (*it).second; + + // load wav file + PCM *pcm = Wav::load(name); + if (!pcm) { + registry[name] = 0; + return 0; + } + + if (index == MAXBUFFERS) { + con_error << "Buffer limit " << MAXBUFFERS << " exceeded!" << std::endl; + delete pcm; + registry[name] = 0; + return 0; + } + + ALenum format = 0; + if (pcm->bitspersample() == 16) { + if (pcm->channels() == 1) { + format = AL_FORMAT_MONO16; + } else if (pcm->channels() == 2) { + format = AL_FORMAT_STEREO16; + }; + } else if (pcm->bitspersample() == 8) { + if (pcm->channels() == 1) { + format = AL_FORMAT_MONO8; + } else if (pcm->channels() == 2) { + format = AL_FORMAT_STEREO8; + }; + } + + size_t id = index; + alBufferData(buffers[id], format, pcm->data(), pcm->size(), pcm->samplerate()); + if (alGetError()) + con_warn << "Error loading PCM data " << name << std::endl; + + registry[name] = id; + index++; + + return id; +} + +size_t Buffers::find(std::string name) +{ + size_t id = 0; + iterator it = registry.find(name); + if (it != registry.end()) + id = (*it).second; + return id; +} + +void Buffers::bind(ALuint source, std::string name) +{ + int error; + size_t id = find(name); + alSourcei(source, AL_BUFFER, buffers[id]); + if ((error = alGetError()) != AL_NO_ERROR) { + con_warn << "Error " << std::hex << error << " binding " << name + << " source " << source << " buffer " << buffers[id] << std::endl; + } +} + +} diff --git a/src/audio/buffers.h b/src/audio/buffers.h new file mode 100644 index 0000000..e61ba7b --- /dev/null +++ b/src/audio/buffers.h @@ -0,0 +1,47 @@ +/* + audio/buffers.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_AUDIO_BUFFERS_H__ +#define __INCLUDED_AUDIO_BUFFERS_H__ + +#include "AL/al.h" +#include "AL/alc.h" + +#include +#include + +namespace audio { + +const size_t MAXBUFFERS = 128; + +/// OpenAL buffers wrapper class + +class Buffers { +public: + static void init(); + static void shutdown(); + static void clear(); + + /// find previously loaded PCM data + static size_t find(std::string name); + + /// load audio data into a buffer ans return the buffer index + static size_t load(std::string name); + + /// bind a buffer to a source + static void bind(ALuint source, std::string name); + +private: + typedef std::map::iterator iterator; + + static std::map registry; + static size_t index; + static ALuint buffers[MAXBUFFERS]; +}; + +} + +#endif // __INCLUDED_AUDIO_BUFFERS_H__ diff --git a/src/audio/pcm.cc b/src/audio/pcm.cc new file mode 100644 index 0000000..b485199 --- /dev/null +++ b/src/audio/pcm.cc @@ -0,0 +1,49 @@ +/* + audio/pcm.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include +#include + +#include "audio/pcm.h" +#include "audio/wav.h" + +namespace audio { + +PCM::PCM(unsigned int samplerate, unsigned int bitspersample, unsigned int channels, size_t size) +{ + pcm_bitspersample = bitspersample; + pcm_samplerate = samplerate; + pcm_size = size; + pcm_channels = channels; + + pcm_data = (unsigned char *) malloc(pcm_size); + clear(); +} + +PCM::~PCM() +{ + free(pcm_data); +} + +void PCM::clear() +{ + memset(pcm_data, 0, pcm_size); +} + +void PCM::load(const char *name) +{ + PCM *pcm = Wav::load(name); + if (pcm) { + delete pcm; + } +} + +void PCM::load(std::string const & name) +{ + load(name.c_str()); +} + +} diff --git a/src/audio/pcm.h b/src/audio/pcm.h new file mode 100644 index 0000000..740a23c --- /dev/null +++ b/src/audio/pcm.h @@ -0,0 +1,57 @@ +/* + audio/pcm.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_AUDIO_PCM_H__ +#define __INCLUDED_AUDIO_PCM_H__ + +#include + +namespace audio { + +/// class to hold PCM audio data +class PCM { + +public: + PCM(unsigned int samplerate, unsigned int samplesize, unsigned int channels, size_t size); + ~PCM(); + + void clear(); + + /// pointer to the raw pcm data + inline unsigned char *data() { return pcm_data; } + + /// index into the raw pcm data + inline unsigned char *operator[](size_t index) { return &pcm_data[index]; } + + /// size in bytes + inline size_t size() { return pcm_size; } + + /// samplerate in samples per second + inline unsigned int samplerate() const { return pcm_samplerate; } + + /// number of bits per sample + inline unsigned int bitspersample() const { return pcm_bitspersample; } + + /// number of channels + inline unsigned int channels() const { return pcm_channels; } + + /// load PCM audio data from file + static void load(std::string const & name); + + static void load(const char *name); + +private: + unsigned char *pcm_data; + + unsigned int pcm_channels; + unsigned int pcm_samplerate; + unsigned int pcm_bitspersample; + size_t pcm_size; +}; + +} + +#endif // __INCLUDED_AUDIO_PCM_H__ diff --git a/src/audio/wav.cc b/src/audio/wav.cc new file mode 100644 index 0000000..5d118c6 --- /dev/null +++ b/src/audio/wav.cc @@ -0,0 +1,119 @@ +/* + audio/wav.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +/* +see + +http://ccrma.stanford.edu/CCRMA/Courses/422/projects/WaveFormat/ +*/ + +#include "string.h" + +#include +#include + +#include "audio/wav.h" +#include "filesystem/filesystem.h" +#include "sys/sys.h" + +namespace audio { + +PCM *Wav::load(std::string const & name) +{ + if (!name.size()) + return 0; + + std::string filename("sounds/"); + filename.append(name); + filename.append(".wav"); + + filesystem::File *wav_file = filesystem::open(filename.c_str()); + if (!wav_file) { + con_warn << "Could not open " << filename << std::endl; + return 0; + } + + unsigned char header[44]; + memset(header, 0, sizeof(header)); + if (!wav_file->read(header, 44)) { + con_warn << "Error reading " << filename << std::endl; + filesystem::close(wav_file); + return 0; + } + + // header RIFF + if (strncmp((char *)header, "RIFF", 4)) { + con_warn << "Error reading " << filename << ": invalid RIFF header!" << std::endl; + filesystem::close(wav_file); + return 0; + } + + // format WAVE + if (strncmp((char *)header+8, "WAVE", 4) != 0) { + con_warn << "Error reading " << filename << ": invalid WAVE header!" << std::endl; + filesystem::close(wav_file); + return 0; + } + + // file size + //size_t chunksize = header[4] + (header[5] << 8) + (header[6] << 16) + (header[7] << 24); + + if (strncmp((char *)header+12, "fmt ", 4) != 0) { + con_warn << "Error reading " << filename << ": invalid format header!" << std::endl; + filesystem::close(wav_file); + return 0; + } + + size_t subchunksize1 = header[16] + (header[17] << 8) + (header[18] << 16) + (header[19] << 24); + size_t audioformat = header[20] + (header[21] << 8); + + if ((subchunksize1 != 16) || (audioformat != 1)) { + con_warn << "Error reading " << filename << ": unrecognized file format!" << std::endl; + filesystem::close(wav_file); + return 0; + + } + + unsigned int channels = header[22] + (header[23] << 8); + if ((channels < 1) || (channels >2)) { + con_warn << "Error reading " << filename << ": invalid number of channels!" << std::endl; + filesystem::close(wav_file); + return 0; + } + + unsigned int samplerate = header[24] + ((size_t)header[25] << 8) + ((size_t)header[26] << 16) + ((size_t)header[27] << 24); + //size_t byterate = header[28] + (header[29] << 8) + (header[30] << 16) + (header[31] << 24); + //size_t blockalign = header[32] + (header[33] << 8); + size_t bitspersample = header[34] + (header[35] << 8); + + if (strncmp((char *)header + 36, "data", 4) !=0) { + con_warn << "Error reading " << filename << ": invalid data header!" << std::endl; + filesystem::close(wav_file); + return 0; + } + + size_t datasize = header[40] + (header[41] << 8) + (header[42] << 16) + (header[43] << 24); + + PCM *pcm = new PCM(samplerate, bitspersample, channels, datasize); + const size_t readblocksize = channels * (bitspersample / 8); + size_t total_bytes = 0; + + while ((wav_file->read((void *)(*pcm)[total_bytes], readblocksize)) && (total_bytes < datasize)) { + total_bytes += readblocksize; + } + + if (total_bytes < datasize) { + con_warn << "Error reading " << filename << ": file truncated!" << std::endl; + } + + con_print << " " << filename << " " << pcm->samplerate()<< "Hz " << pcm->bitspersample() << "bit " << + pcm->channels() << " chan " << pcm->size() << " bytes" << std::endl; + + filesystem::close(wav_file); + return pcm; +} + +} diff --git a/src/audio/wav.h b/src/audio/wav.h new file mode 100644 index 0000000..a94b6fb --- /dev/null +++ b/src/audio/wav.h @@ -0,0 +1,22 @@ +/* + audio/wav.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_AUDIO_WAV_H__ +#define __INCLUDED_AUDIO_WAV_H__ + +#include "audio/pcm.h" + +namespace audio { + +/// class to read microsoft PCM wav files +class Wav { +public: + static PCM *load(std::string const & name); +}; + +} + +#endif // __INCLUDED_AUDIO_WAV_H__ diff --git a/src/client/client.cc b/src/client/client.cc index 85dec27..ed31c65 100644 --- a/src/client/client.cc +++ b/src/client/client.cc @@ -10,6 +10,7 @@ #include #include +#include "audio/audio.h" #include "client/chat.h" #include "client/client.h" #include "client/video.h" @@ -23,26 +24,6 @@ namespace client { core::Cvar *cl_framerate = 0; - -//--- private definition ------------------------------------------ - -/// client application implementation -class Client : public core::Application -{ -public: - /// initialize the client Client - virtual void init(int count, char **arguments); - - /// run the client Client - virtual void run(); - - /// shutdown the client Client - virtual void shutdown(); - - /// quit the client Client - virtual void quit(int status); -}; - Client app; //--- engine functions -------------------------------------------- @@ -71,6 +52,11 @@ void client_main(int count, char **arguments) app.shutdown(); } +Client *client() +{ + return &app; +} + //--- private ----------------------------------------------------- void Client::quit(int status) @@ -112,6 +98,9 @@ void Client::init(int count, char **arguments) // initialize input input::init(); + + // initialize audio + audio::init(); // add engine functions core::Func *func = 0; @@ -147,8 +136,6 @@ void Client::run() } }; - //con_debug << "tick " << std::setw(8) << chrono << " " << std::setw(8) << current << " " << elapsed; - } } @@ -157,11 +144,12 @@ void Client::shutdown() { con_print << "^BShutting down client..." << std::endl; - // remove engine functions core::Func::remove("r_restart"); chat::shutdown(); + audio::shutdown(); + Console::shutdown(); input::shutdown(); @@ -173,5 +161,10 @@ void Client::shutdown() quit(0); } +void Client::notify_sound(const char * name) +{ + audio::play(name); +} + } // namespace client diff --git a/src/client/client.h b/src/client/client.h index b1a50df..57488ec 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -7,12 +7,37 @@ #ifndef __INCLUDED_CLIENT_H__ #define __INCLUDED_CLIENT_H__ +#include "core/application.h" + /// client part of the engine namespace client { +/// client application implementation +class Client : public core::Application +{ +public: + /// initialize the client + virtual void init(int count, char **arguments); + + /// run the client + virtual void run(); + + /// shutdown the client + virtual void shutdown(); + + /// quit the client + virtual void quit(int status); + + /// sound notifications from the core to the application + virtual void notify_sound(const char * name); +}; + + /// the client main loop void client_main(int count, char **arguments); +Client *client(); + } #endif // __INCLUDED_CLIENT_H__ diff --git a/src/client/console.cc b/src/client/console.cc index f692db4..ac977f2 100644 --- a/src/client/console.cc +++ b/src/client/console.cc @@ -245,9 +245,9 @@ void Console::draw_notify() Text::setcolor('N'); size_t width = (size_t) ((video::width-8) / Text::fontwidth()); size_t n = notify_pos % MAXNOTIFYLINES; - float h = 4 + 2*Text::fontheight(); + float h = video::height/2; for (size_t l = 0; l < MAXNOTIFYLINES; l++) { - if (notify_text[n].size() > 2 && notify_time[n] + 4 > core::application()->time()) { + if (notify_text[n].size() > 2 && notify_time[n] + 5 > core::application()->time()) { std::string linedata(notify_text[n]); linedata += '\n'; diff --git a/src/client/console.h b/src/client/console.h index 2f8d6a0..5bdbde6 100644 --- a/src/client/console.h +++ b/src/client/console.h @@ -11,7 +11,7 @@ namespace client { -const size_t MAXNOTIFYLINES = 3; +const size_t MAXNOTIFYLINES = 5; const size_t MAXHISTOLINES = 512; /// client console implementation diff --git a/src/client/view.cc b/src/client/view.cc index e2c8687..5c12710 100644 --- a/src/client/view.cc +++ b/src/client/view.cc @@ -138,8 +138,8 @@ void draw_status() int minutes = (int) floorf(core::game()->clientframetime() / 60.0f); int seconds = (int) floorf( core::game()->clientframetime() - (float) minutes* 60.0f); - status << "^Ntime ^B" << std::setfill('0') << std::setw(2) << minutes << "^N:^B" << std::setfill('0') << std::setw(2) << seconds; - Text::draw(4, 4, status); + status << "^Ntime ^B" << std::setfill('0') << std::setw(2) << minutes << ":" << std::setfill('0') << std::setw(2) << seconds; + Text::draw(video::width-Text::fontwidth()*11-4, 4+Text::fontheight(), status); } // print stats if desired @@ -152,7 +152,7 @@ void draw_status() } stats << "^Ntx ^B"<< std::setw(5) << (core::Stats::network_bytes_sent >> 10) << "\n"; stats << "^Nrx ^B"<< std::setw(5) << (core::Stats::network_bytes_received >> 10) << "\n"; - Text::draw(video::width-Text::fontwidth()*12, video::height - Text::fontheight()*8, stats); + Text::draw(video::width-Text::fontwidth()*11-4, 4 + Text::fontheight()*3, stats); } // draw a basic HUD diff --git a/src/core/application.cc b/src/core/application.cc index 67bb952..db97af8 100644 --- a/src/core/application.cc +++ b/src/core/application.cc @@ -377,4 +377,15 @@ void Application::load_commandline(int count, char **arguments) cmd() << '\n'; } +void Application::notify_sound(const char *name) +{ + // the default implementation does nothing. + // Dedicated servers don't need sounds +} + +void Application::notify_message(std::string const & message) +{ + con_print << message << std::endl; +} + } diff --git a/src/core/application.h b/src/core/application.h index bec53d9..aad3ac9 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -56,6 +56,14 @@ public: /// quit the application without proper shutdown virtual void quit(int status); +/*----- notifications --------------------------------------------- */ + + /// sound notifications from the core to the application + virtual void notify_sound(const char * name); + + /// text notifications from the core to the application + virtual void notify_message(std::string const & message); + /*----- static --------------------------------------------------- */ /// a pointer to the current application instance diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index fb828e2..54d667b 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -8,6 +8,7 @@ #include "auxiliary/functions.h" #include "sys/sys.h" +#include "core/application.h" #include "core/cvar.h" #include "core/func.h" #include "core/gameserver.h" @@ -143,15 +144,19 @@ void GameServer::say(Player *player, std::string const &message) if (!message.size()) return; - // send to console - con_print << "^B" << player->name() << "^F:^B " << message << std::endl; + std::string notification("^B"); + notification.append(player->name()); + notification.append("^F:^B "); + notification.append(message); + + // send to application + application()->notify_message(notification); + application()->notify_sound("ui/chat.wav"); // broadcast to remote clients if (server_network) { - std::string netmessage("msg public ^B"); - netmessage.append(player->name()); - netmessage.append("^F:^B "); - netmessage.append(message); + std::string netmessage("msg public "); + netmessage.append(notification); netmessage += '\n'; server_network->broadcast(netmessage); } @@ -159,8 +164,8 @@ void GameServer::say(Player *player, std::string const &message) void GameServer::broadcast(std::string const & message, Player *ignore_player) { - // send to console - con_print << message << "\n"; + // send to application + application()->notify_message(message); // broadcast to remote clients if (server_network) { @@ -173,9 +178,9 @@ void GameServer::broadcast(std::string const & message, Player *ignore_player) void GameServer::send(Player *player, std::string message) { - // send to console + // send to application if (player->id() == localplayer()->id()) { - con_print << message << "\n"; + application()->notify_message(message); } // send to remote clients diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index 22614d2..8da8b1b 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -283,12 +283,13 @@ void NetConnection::parse_incoming_message(const std::string & message) if (msgstream >> level) { if (level =="info") { if (message.size() > 9) { - con_print << message.substr(9) << std::endl; + application()->notify_message(message.substr(9)); } } else if (level == "public") { // FIXME - separate sender nickname if (message.size() > 11) { - con_print << message.substr(11) << std::endl; + application()->notify_message(message.substr(11)); + application()->notify_sound("ui/chat.wav"); } } diff --git a/src/model/map.cc b/src/model/map.cc index 03ebe69..fea98e0 100644 --- a/src/model/map.cc +++ b/src/model/map.cc @@ -172,7 +172,7 @@ Model * Map::load(std::string const &name) } - con_print << " maps/" << model->name() << ".map " << mapfile.brushes << " brush " << model->tris() + con_print << " maps/" << model->name() << ".map " << mapfile.brushes << " brushes " << model->tris() << " tris (" << model->details() << " detail)" << std::endl; return model; diff --git a/src/render/image.h b/src/render/image.h index 0c1069b..a129773 100644 --- a/src/render/image.h +++ b/src/render/image.h @@ -15,31 +15,31 @@ public: Image(unsigned int width, unsigned int height, unsigned int channels); ~Image(); - // pointer to the image data + /// pointer to the raw image data inline unsigned char *data() { return image_data; } - // index into the image data + /// index into the raw image data inline unsigned char *operator[](size_t index) { return &image_data[index]; } - // width of the image in pixels + /// width of the image in pixels inline unsigned int width() const { return image_width; } - // height of the image in pixels + /// height of the image in pixels inline unsigned int height() const { return image_height; } - // size of the image data in bytes + /// size of the image data in bytes inline size_t size() const { return ((size_t) image_width * (size_t) image_height * (size_t) image_channels); } - // number of channels 3 (RGB) or 4 (RGBA) + /// number of channels 3 (RGB) or 4 (RGBA) inline unsigned int channels() const { return image_channels; } - // set image data to zero + /// set image data to zero void clear(); - // swap the red and blue channel values of the image + /// swap the red and blue channel values of the image void swap_channels(); - // flip upside-down + /// flip upside-down void flip(); private: -- cgit v1.2.3