diff options
author | Stijn Buys <ingar@osirion.org> | 2008-05-18 09:21:20 +0000 |
---|---|---|
committer | Stijn Buys <ingar@osirion.org> | 2008-05-18 09:21:20 +0000 |
commit | 4a2bad92171ff8a9a248599f47087cfe39e93653 (patch) | |
tree | b8a4fe7f616b3e4f707d89a35fff5e8b5fdcfcc8 /src/audio | |
parent | a185c11f2397c0296a4b62cc266b4fa00a63c1e2 (diff) |
OpenAL support
Diffstat (limited to 'src/audio')
-rw-r--r-- | src/audio/Makefile.am | 6 | ||||
-rw-r--r-- | src/audio/audio.cc | 80 | ||||
-rw-r--r-- | src/audio/audio.h | 28 | ||||
-rw-r--r-- | src/audio/buffers.cc | 112 | ||||
-rw-r--r-- | src/audio/buffers.h | 47 | ||||
-rw-r--r-- | src/audio/pcm.cc | 49 | ||||
-rw-r--r-- | src/audio/pcm.h | 57 | ||||
-rw-r--r-- | src/audio/wav.cc | 119 | ||||
-rw-r--r-- | src/audio/wav.h | 22 |
9 files changed, 520 insertions, 0 deletions
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<std::string, size_t> 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 <string> +#include <map> + +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<std::string, size_t>::iterator iterator; + + static std::map<std::string, size_t> 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 <stdlib.h> +#include <string.h> + +#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 <string> + +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 <iostream> +#include <string> + +#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__ |