First attempt at OpenAL (thanks to ChrisRobinson)

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@20 ea6a568a-9f4f-0410-981a-c910a81bb256
actorid
nkorslund 16 years ago
parent fa6f5b81ae
commit 6988814728

@ -37,8 +37,8 @@ Dependencies:
Dependencies needed to build OpenMW:
OGRE 1.4.5 (3d engine)
Audiere 1.9.4 (sound engine)
OIS-1.0.0 (input system)
OpenAL (3d sound system)
gcc and g++ (C++ compiler)
GNU make (build tool for C++ files)
DMD 1.031 (D compiler)
@ -46,15 +46,15 @@ DMD 1.031 (D compiler)
The above versions are the ones I have tested recently, but other
versions might work. OGRE, Audiere and OIS will require their own set
of dependencies. I recommend using an automated package tool to
install as many of these as possible. On ubuntu, try typing:
versions might work. OGRE and OIS will require their own set of
dependencies. I recommend using an automated package tool to install
as many of these as possible. On ubuntu, try typing:
sudo apt-get install libogre-dev libaudiere-dev libois-dev build-essential g++ gdc
If you want to install Ogre, Audiere or OIS manually, try:
sudo apt-get install libogre-dev libalut0 libois-dev build-essential g++ gdc
If you want to install Ogre, OpenAL or OIS manually, try:
OGRE: http://ogre3d.org
Audiere: http://audiere.sourceforge.net/
OGRE: http://ogre3d.org/
Audiere: http://openal.org/
OIS: http://sourceforge.net/projects/wgois/
@ -87,11 +87,19 @@ installed (a D build tool), type:
dsss build
If you do NOT have DSSS, you can compile using the script
If you do NOT have DSSS, try using make
make all
You might need to edit the Makefile to match your setup. If you are
using DMD instead of GDC, try changing the compiler from "gdmd" to
"dmd" in the Makefile.
If all else fails, you can try the build script:
./build_openmw.sh
(Currently only works with the GDC compiler)
This build method is deprecated and only works with gdc.

@ -3,28 +3,81 @@
# Compiler settings
CXX?= g++
CXXFLAGS?= -Wall -g
DMD=gdmd -version=Posix
# Compiler settings for Ogre + OIS. Change as needed.
OGCC=$(CXX) $(CXXFLAGS) `pkg-config --cflags OGRE OIS`
# Some extra flags for niftool and bsatool
NIFFLAGS=
# Compiler settings for Audiere
AGCC=$(CXX) $(CXXFLAGS) `audiere-config --cxxflags`
# Compiler settings for Ogre + OIS. Change as needed.
OGCC=$(CXX) $(CXXFLAGS) `pkg-config --cflags OGRE OIS openal`
# Ogre C++ files, on the form ogre/cpp_X.cpp. Only the first file is
# passed to the compiler, the rest are dependencies.
ogre_cpp=ogre framelistener interface overlay bsaarchive
# Audiere C++ files, on the form sound/cpp_X.cpp.
audiere_cpp=audiere
## The rest of this file is automatic ##
ogre_cpp_files=$(ogre_cpp:%=ogre/cpp_%.cpp)
audiere_cpp_files=$(audiere_cpp:%=sound/cpp_%.cpp)
all: cpp_ogre.o cpp_audiere.o
d_files=$(wildcard */*.d) $(wildcard monster/util/*.d)
d_files_nif=$(wildcard nif/*.d) $(wildcard util/*.d) $(wildcard core/memory.d) $(wildcard monster/util/*.d)
# The NIF object files for niftool and bsatool are put in a separate
# directory, since they are built with different flags.
d_objs=$(d_files:%.d=objs/%.o)
d_objs_nif=$(d_files_nif:%.d=nifobjs/%.o)
.PHONY: cpp all clean makedirs
# By default, make will only build the Ogre C++ sources.
cpp: cpp_ogre.o
all: makedirs openmw esmtool niftool bsatool bored
cpp_ogre.o: $(ogre_cpp_files)
$(OGCC) -c $<
cpp_audiere.o: $(audiere_cpp_files)
$(AGCC) -c $<
objs/%.o: %.d
$(DMD) -c $< -of$@
nifobjs/%.o: %.d
$(DMD) -debug=warnstd -debug=check -debug=statecheck -debug=strict -debug=verbose -c $< -of$@
# This is a hack for gdmd (dmd-like frontend to gdc), since it does
# not automatically create directories as it should.
makedirs:
mkdir -p objs/bsa
mkdir -p objs/core
mkdir -p objs/esm
mkdir -p objs/input
mkdir -p objs/monster/util
mkdir -p objs/nif
mkdir -p objs/ogre
mkdir -p objs/scene
mkdir -p objs/sound
mkdir -p objs/util
mkdir -p nifobjs/nif
mkdir -p nifobjs/util
mkdir -p nifobjs/core
mkdir -p nifobjs/monster/util
mkdir -p nifobjs/bsa
openmw: openmw.d cpp_ogre.o $(d_objs)
$(DMD) $^ -of$@ -L-lalut -L-lopenal -L-lOgreMain -L-lOIS
esmtool: esmtool.d cpp_ogre.o $(d_objs)
$(DMD) $^ -of$@ -L-lalut -L-lopenal -L-lOgreMain -L-lOIS
niftool: niftool.d $(d_objs_nif)
$(DMD) $^ -of$@
bsatool: bsatool.d $(d_objs_nif) bsa/bsafile.d
$(DMD) $^ -of$@
bored: bored.d
$(DMD) $^
clean:
-rm cpp_ogre.o
-rm openmw esmtool niftool bsatool bored
-rm -r objs/ nifobjs/ dsss_objs/

@ -5,8 +5,8 @@ Written by Nicolay Korslund
Email: korslund@gmail.com
WWW: http://openmw.snaptoad.com
License: See GPL3.txt
Current version: 0.3 (still very pre-alpha)
Date: 2008 jul. 10
Current version: 0.4 (still very pre-alpha)
Date: 2008 jul. 12
@ -18,16 +18,24 @@ Morrowind installed on your system!
Release notes for 0.3
=====================
IMPORTANT: Subversion notes
===========================
The subversion code is currently in the process of switching from
Audiere to OpenAL. This means that:
- you need to install OpenAL and ALUT
- you no longer need Audiere
- music does not (currently) work
Generally true for all SVN versions is that:
As of this release, OpenMW officially builds and runs on Windows. The
installation instructions have been split into the files
COMPILE-win32.txt and README-win32.txt for the source and binary
windows releases respectively, and COMPILE-linux.txt for Linux / Unix
systems.
- a given SVN revision is not guaranteed to work or compile
- windows compilation scripts are unlikely to work since they are
updated less often
- README and instructions might be out of date
See the changelog at the end for more changes.
See the changelog at the end for an up-to-date list of changes.
Note: if you are using a localized (non-English) version of Morrowind,
the default starting cell (Sud) might not exist, and the esmtool
@ -105,14 +113,18 @@ Thanks goes out to:
- Bastien Jansen for continued testing on 64 bit linux.
- Bethesda Softworks for creating Morrowind!
- Chris Robinson for OpenAL and ALUT support
Changelog:
==========
0.4 (Work in progress)
- switched from Audiere to OpenAL (BIG thanks to Chris Robinson)
- added complete Makefile (again) as a alternative build tool
- cosmetic changes to placate gdc -Wall
0.3 (2008 jul. 10) - latest release
- built and tested on Windows XP
- partial support for FreeBSD (exceptions do not work)

@ -1,9 +1,9 @@
@echo off
rem See BUILDING-win32.txt for instructions.
rem See COMPILE-win32.txt for instructions.
rem This file assumes it can find Ogre in ..\ogre and
rem Audiere in ..\audiere
rem This file assumes it can find Ogre in ..\ogro
rem TODO: Not updated to OpenAL
echo Compiling C++ files
g++ -c sound\cpp_audiere.cpp -I..\audiere\include
@ -16,4 +16,4 @@ copy ..\audiere\bin\audiere.dll .
copy \windows\system32\d3dx9_30.dll d3dx9d_30.dll
echo Compiling main program (openmw.exe)
gdc openmw.d bsa\*.d core\*.d esm\*.d input\*.d nif\*.d ogre\*.d scene\*.d sound\*.d util\*.d cpp_audiere.o cpp_ogre.o monster\util\*.d ogremain_d.dll ..\audiere\lib\audiere.lib OIS_d.dll -lstdc++ -o openmw.exe
gdc -Wall -g openmw.d bsa\*.d core\*.d esm\*.d input\*.d nif\*.d ogre\*.d scene\*.d sound\*.d util\*.d cpp_ogre.o monster\util\*.d ogremain_d.dll OIS_d.dll -lstdc++ -o openmw.exe

@ -1,9 +1,9 @@
#!/bin/sh
# See INSTALL-linux.txt for instructions
# See COMPILE-linux.txt for instructions
make || exit 1
gdc -Wall -Wextra -O2 -g -fversion=Posix -o openmw openmw.d core/*.d ogre/*.d nif/*.d util/*.d bsa/*.d monster/util/*.d input/*.d sound/*.d scene/*.d esm/*.d cpp_*.o -laudiere -lm -lOgreMain -lOIS -lstdc++
gdc -Wall -g -fversion=Posix -o openmw openmw.d core/*.d ogre/*.d nif/*.d util/*.d bsa/*.d monster/util/*.d input/*.d sound/*.d scene/*.d esm/*.d cpp_ogre.o -lalut -lopenal -lm -lOgreMain -lOIS -lstdc++
gdc -Wall -Wextra -O2 -g -fversion=Posix -o esmtool esmtool.d core/*.d ogre/*.d nif/*.d util/*.d bsa/*.d monster/util/*.d input/*.d sound/*.d scene/*.d esm/*.d cpp_*.o -laudiere -lm -lOgreMain -lOIS -lstdc++
gdc -Wall -g -fversion=Posix -o esmtool esmtool.d core/*.d ogre/*.d nif/*.d util/*.d bsa/*.d monster/util/*.d input/*.d sound/*.d scene/*.d esm/*.d cpp_ogre.o -lalut -lopenal -lm -lOgreMain -lOIS -lstdc++

@ -4,7 +4,7 @@
[openmw.d]
# Add libraries and the two C++ object files
buildflags = -llOgreMain -llaudiere -llOIS cpp_audiere.o cpp_ogre.o
buildflags = -llOgreMain -llalut -llopenal -llOIS cpp_ogre.o
# Make sure the C++ object files are built first
version(Windows) {
@ -34,7 +34,7 @@ prebuild += dsss clean niftool
[esmtool.d]
# Because of interdepencies between the ESM code and the resource
# manager, we have to include all the C++ stuff.
buildflags = -llOgreMain -llaudiere -llOIS cpp_audiere.o cpp_ogre.o
buildflags = -llOgreMain -llalut -llopenal -llOIS cpp_ogre.o
@ -47,6 +47,4 @@ buildflags = -debug=warnstd -debug=check -debug=statecheck -debug=strict -debug=
# Clean the nif object files to make sure they are recompiled in debug mode
prebuild = dsss clean niftool
[bored.d]

@ -87,12 +87,12 @@ struct TES3FileContext
{
char[] filename;
uint leftRec, leftSub;
size_t leftFile;
ulong leftFile;
NAME recName, subName;
FileType type;
Version ver;
size_t filepos;
ulong filepos;
}
/**
@ -118,7 +118,7 @@ struct TES3File
// These are only used by getRecHeader and getSubHeader for
// asserting the file's integrity.
uint leftFile; // Number of unread bytes in file
ulong leftFile; // Number of unread bytes in file
uint leftRec; // Number of unread bytes in record
// This is used by sub-record readers for integrity checking.

@ -154,6 +154,7 @@ void main(char[][] args)
case WT.MarksmanThrown: writef("Thrown weapon"); break;
case WT.Arrow: writef("Arrow"); break;
case WT.Bolt: writef("Bolt"); break;
default: assert(0);
}
writefln(" id '%s': name '%s'", n, m.name);
@ -278,6 +279,7 @@ void printRaw()
case FileType.Master: writefln("Master"); break;
case FileType.Savegame: writefln("Savegame"); break;
case FileType.Unknown: writefln("Unknown"); break;
default: assert(0);
}
writef("Version: ");
if(isVer12()) writefln("1.2");

@ -309,9 +309,13 @@ extern(C) int d_frameStarted(float time)
if(sndCumTime > sndRefresh)
{
float x, y, z;
float fx, fy, fz;
float ux, uy, uz;
cpp_getCameraPos(&x, &y, &z);
cpp_getCameraOrientation(&fx, &fy, &fz, &ux, &uy, &uz);
soundScene.update(x,y,z);
soundScene.update(x,y,z,fx,fy,fz,ux,uy,uz);
sndCumTime -= sndRefresh;
}

@ -167,7 +167,7 @@ class NiAutoNormalParticlesData : ShapeData
debug(verbose) writef("Active vertices: ");
// Number of active vertices (always matches count?)
activeCount = nifFile.wgetUshortIs(cast(ushort)vertices.length/3);
activeCount = nifFile.wgetUshortIs(cast(ushort)(vertices.length/3));
nifFile.wgetFloat(); // Active radius (?)
nifFile.wgetShort(); // Number of valid entries in the following arrays

@ -140,10 +140,11 @@ void cpp_createMesh(
// Save a screen shot to the given file name
void cpp_screenshot(char *filename);
// Camera controll
// Camera control and information
void cpp_rotateCamera(float x, float y);
void cpp_moveCamera(float x, float y, float z, float r1, float r2, float r3);
void cpp_getCameraPos(float *x, float *y, float *z);
void cpp_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz);
void cpp_moveCameraRel(float x, float y, float z);
// Do some debug action. Check the menu for today's specials!

@ -132,6 +132,20 @@ extern "C" void cpp_getCameraPos(float *x, float *y, float *z)
*z = pos[1];
}
// Get current camera orientation
extern "C" void cpp_getCameraOrientation(float *fx, float *fy, float *fz,
float *ux, float *uy, float *uz)
{
Vector3 front = mCamera->getDirection();
Vector3 up = mCamera->getUp();
*fx = front[0];
*fy = -front[2];
*fz = front[1];
*ux = up[0];
*uy = -up[2];
*uz = up[1];
}
// Move and rotate camera in place. Transforms Morrowind coordinates
// to OGRE coordinates.
extern "C" void cpp_moveCamera(float x, float y, float z,

@ -359,9 +359,15 @@ void main(char[][] args)
// Run it until the user tells us to quit
startRendering();
jukebox.disableMusic();
battleMusic.disableMusic();
}
else debug(verbose) writefln("Skipping rendering");
soundScene.kill();
shutdownSound();
debug(verbose)
{
writefln();

@ -37,10 +37,7 @@ SoundList soundScene;
// very long.
struct SoundList
{
// TODO: This is really just a test, a hack. Will be replaced by a
// list or similar later.
SoundInstance list[50];
int index = 0;
SoundInstance[] list;
// Get a sound instance from a Sound struct
static SoundInstance getInstance(Sound *s, bool loop=false)
@ -57,16 +54,33 @@ struct SoundList
SoundInstance *insert(Sound *snd, bool loop=false)
{
if(index == 50) return null;
// Reuse a dead instance if one exists
foreach(ref s; list)
{
if(s.owner == null)
{
s = getInstance(snd, loop);
return &s;
}
}
// Otherwise append a new one
list ~= getInstance(snd, loop);
return &list[$-1];
}
SoundInstance *s = &list[index++];
*s = getInstance(snd, loop);
return s;
void update(float x, float y, float z,
float frontx, float fronty, float frontz,
float upx, float upy, float upz)
{
SoundInstance.setPlayerPos(x,y,z,frontx,fronty,frontz,upx,upy,upz);
}
void update(float x, float y, float z)
void kill()
{
foreach(ref s; list[0..index])
s.setPlayerPos(x,y,z);
foreach(ref s; list)
{
if(s.owner) s.kill();
}
list = null;
}
}

@ -1,3 +1,5 @@
module sound.al;
extern(System):
//Defines

@ -1,3 +1,5 @@
module sound.alc;
extern(System):
//Definitions

@ -1,72 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (audiere.d) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
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.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
// This file exposes the C++ functions that deal directly with the
// sound library. The functions are implemented in cpp_audiere.cpp
module sound.audiere;
// A sound resource is the handle that represents the resource,
// ie. the file.
typedef void* AudiereResource;
// A sound instance is an instance of this resource. We can have
// several instances of the same file playing at once. Each instance
// has its own volume, file position, pitch, etc.
typedef void* AudiereInstance;
extern(C)
{
// Open the music device. Returns 0 on success, 1 on failure.
int cpp_openDevice();
// Open a new sound resource. Returns null on failure.
AudiereResource cpp_openSound(char* filename);
// Close a resource.
void cpp_closeSound(AudiereResource sound);
// Create an instance of a sound.
AudiereInstance cpp_createInstance(AudiereResource sound);
// Create an instance by streaming directly from file, and play it.
AudiereInstance cpp_playStream(char* filename, float volume);
// Destroy a previously created instance
void cpp_destroyInstance(AudiereInstance instance);
// Is this instance currently playing?
int cpp_isPlaying(AudiereInstance sound);
// Adjust parameters for this instance.
void cpp_setParams(AudiereInstance sound, float volume, float pan);
// Play a sound.
void cpp_playSound(AudiereInstance sound);
// Set repeat mode on
void cpp_setRepeat(AudiereInstance sound);
// Stop a sound
void cpp_stopSound(AudiereInstance sound);
}

@ -26,7 +26,15 @@ module sound.audio;
public import sound.sfx;
public import sound.music;
import sound.audiere;
import sound.al;
import sound.alc;
ALCdevice *Device = null;
ALCcontext *Context = null;
// Temporarilly use ALUT for data loading until something better is picked
extern (C) ALboolean alutInitWithoutContext(int *argc, char **argv);
extern (C) ALboolean alutExit();
class SoundException : Exception
{
@ -38,9 +46,40 @@ MusicManager battleMusic;
void initializeSound()
{
if(cpp_openDevice())
Device = alcOpenDevice(null);
Context = alcCreateContext(Device, null);
if(!Device || !Context)
throw new SoundException("initializeSound()",
"Failed to initialize music device");
alcMakeContextCurrent(Context);
alutInitWithoutContext(null, null);
// Gross HACK: We should use the default model (inverse distance clamped).
// But without a proper rolloff factor, distance attenuation is completely
// wrong. This at least makes sure the max distance is the 'silence' point
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
jukebox.initialize("Main");
battleMusic.initialize("Battle");
}
void shutdownSound()
{
alutExit();
alcMakeContextCurrent(null);
if(Context) alcDestroyContext(Context);
Context = null;
if(Device) alcCloseDevice(Device);
Device = null;
}
ALenum checkALError()
{
ALenum err = alGetError();
if(err != AL_NO_ERROR)
writefln("WARNING: Received AL error (%x): %s", err,
toString(alGetString(err)));
return err;
}

@ -1,118 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (cpp_audiere.cpp) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
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.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
#include <audiere.h>
#include <iostream>
using namespace audiere;
using namespace std;
/*
* Sound, using Audiere. Creates a very simple C interface for
* opening, closing and manipulating sounds.
*/
AudioDevicePtr device;
extern "C" int32_t cpp_openDevice()
{
device = OpenDevice("");
if (!device) return 1;
return 0;
}
// Opens a new sample buffer from a file
extern "C" SampleBuffer *cpp_openSound(char* filename)
{
SampleSourcePtr sample = OpenSampleSource(filename);
if(!sample) return NULL;
SampleBufferPtr buf = CreateSampleBuffer(sample);
buf->ref();
return buf.get();
}
// Delete a sample buffer
extern "C" void cpp_closeSound(SampleBuffer *buf)
{
buf->unref();
}
// Get an output stream from a sample buffer.
extern "C" OutputStream *cpp_createInstance(SampleBuffer *buf)
{
SampleSourcePtr sample = buf->openStream();
if(!sample) return NULL;
OutputStreamPtr sound = OpenSound(device, sample, false);
sound->ref();
return sound.get();
}
// Stream a file directly. Used for music.
extern "C" OutputStream *cpp_playStream(char* filename, float volume)
{
OutputStreamPtr sound = OpenSound(device, filename, true);
if(sound)
{
sound->ref();
sound->setVolume(volume);
sound->play();
}
return sound.get();
}
extern "C" void cpp_destroyInstance(OutputStream *sound)
{
sound->unref();
}
extern "C" int32_t cpp_isPlaying(OutputStream *sound)
{
if(sound && sound->isPlaying()) return 1;
return 0;
}
extern "C" void cpp_setParams(OutputStream *sound, float vol, float pan)
{
if(sound)
{
sound->setVolume(vol);
sound->setPan(pan);
}
}
extern "C" void cpp_playSound(OutputStream *sound)
{
sound->play();
}
extern "C" void cpp_setRepeat(OutputStream *sound)
{
sound->setRepeat(true);
}
// Stop the sound
extern "C" void cpp_stopSound(OutputStream *sound)
{
if(sound) sound->stop();
}

@ -23,34 +23,29 @@
module sound.music;
import sound.audiere;
import sound.audio;
import sound.al;
import std.string;
import core.config;
import core.resource;
extern (C) ALuint alutCreateBufferFromFile(char *filename);
extern (C) ALenum alutGetError();
extern (C) ALchar *alutGetErrorString(ALenum err);
// Simple music player, has a playlist and can pause/resume music.
struct MusicManager
{
private:
// How often we check if the music has died.
const float pollInterval = 2.0;
// Max file size to load. Files larger than this are streamed.
const int loadSize = 1024*1024;
// How much to add to the volume each second when fading
const float fadeInRate = 0.10;
const float fadeOutRate = 0.35;
// Volume
float volume, maxVolume;
// Time since last time we polled
float sumTime;
ALfloat volume, maxVolume;
char[] name;
@ -73,7 +68,10 @@ struct MusicManager
int index; // Index of next song to play
bool musicOn;
AudiereInstance music;
ALuint sID;
ALuint bIDs[1];
ubyte[] readData;
// Which direction are we currently fading, if any
enum Fade { None = 0, In, Out }
@ -85,6 +83,8 @@ struct MusicManager
void initialize(char[] name)
{
this.name = name;
sID = 0;
foreach(ref b; bIDs) b = 0;
musicOn = false;
updateVolume();
}
@ -100,7 +100,7 @@ struct MusicManager
// a fade. Even if we are fading, though, the volume should never
// be over the max.
if(fading == Fade.None || volume > maxVolume) volume = maxVolume;
cpp_setParams(music, volume, 0.0);
alSourcef(sID, AL_GAIN, volume);
}
// Give a music play list
@ -120,7 +120,8 @@ struct MusicManager
{
if(playlist.length < 2) return;
char[] last = playlist[0];
// Get the name of the last song played
char[] last = playlist[(index==0) ? ($-1) : (index-1)];
int left = playlist.length;
rndList.length = left;
@ -166,13 +167,25 @@ struct MusicManager
// If music is disabled, do nothing
if(!musicOn) return;
// Kill current track
if(music) cpp_destroyInstance(music);
music = null;
// No tracks to play?
if(!playlist.length) return;
// Generate a source to play back with if needed
if(!sID)
{
alGenSources(1, &sID);
if(checkALError() != AL_NO_ERROR)
return;
alSourcei(sID, AL_SOURCE_RELATIVE, AL_TRUE);
}
// Kill current track
alSourceStop(sID);
alSourcei(sID, AL_BUFFER, 0);
alDeleteBuffers(bIDs.length, bIDs.ptr);
foreach(ref b; bIDs) b = 0;
checkALError();
// End of list? Randomize and start over
if(index == playlist.length)
{
@ -180,9 +193,35 @@ struct MusicManager
index = 0;
}
music = cpp_playStream(toStringz(playlist[index]), volume);
readData.length = 128*1024 / bIDs.length;
// FIXME: Should load up and queue 3 or 4 buffers here instead of trying to
// load it all into one (when we switch away from ALUT).
char *fname = toStringz(playlist[index]);
bIDs[0] = alutCreateBufferFromFile(fname);
if(!bIDs[0])
{
writefln("Unable to load music track %s: %s", playlist[index],
toString(alutGetErrorString(alutGetError())));
alDeleteSources(1, &sID);
checkALError();
sID = 0;
index++;
return;
}
if(!music) fail("Unable to start music track " ~ playlist[index]);
alSourcei(sID, AL_BUFFER, bIDs[0]);
alSourcePlay(sID);
if(checkALError() != AL_NO_ERROR)
{
writefln("Unable to start music track %s", playlist[index]);
alSourceStop(sID);
alDeleteSources(1, &sID);
alDeleteBuffers(bIDs.length, bIDs.ptr);
checkALError();
sID = 0;
foreach(ref b; bIDs) b = 0;
}
index++;
}
@ -192,7 +231,6 @@ struct MusicManager
{
if(!config.useMusic) return;
sumTime = 0;
musicOn = true;
volume = maxVolume;
fading = Fade.None;
@ -202,9 +240,16 @@ struct MusicManager
// Disable music
void disableMusic()
{
if(music) cpp_destroyInstance(music);
music = null;
musicOn = false;
if(sID)
{
alSourceStop(sID);
alDeleteSources(1, &sID);
alDeleteBuffers(bIDs.length, bIDs.ptr);
checkALError();
sID = 0;
foreach(ref b; bIDs) b = 0;
}
musicOn = false;
}
// Pause current track
@ -218,30 +263,68 @@ struct MusicManager
{
if(!config.useMusic) return;
sumTime = 0;
volume = 0.0;
fading = Fade.In;
musicOn = true;
if(music) cpp_playSound(music);
if(sID) addTime(0);
else playNext();
}
// Add time since last frame to the counter. If the accumulated time
// has passed the polling interval, then check if the music has
// died. The Audiere library has a callback functionality, but it
// turned out not to be terribly reliable. Sometimes it was never
// called at all. So we have to poll at regular intervals .. :( This
// function is also used for fading.
void addTime(float time)
// Checks if a stream is playing, filling more data as needed, and restarting
// if it stalled or was paused.
private bool isPlaying()
{
if(!musicOn) return;
sumTime += time;
if(sumTime > pollInterval)
if(!sID) return false;
/* Use this when we can do streaming..
ALint count;
alGetSourcei(sID, AL_BUFFERS_PROCESSED, &count);
if(checkALError() != AL_NO_ERROR) return false;
for(int i = 0;i < count;i++)
{
sumTime = 0;
if(!cpp_isPlaying(music)) playNext();
int length = GetData(readData.ptr, readData.length);
if(length <= 0)
{
if(i == 0)
{
ALint state;
alGetSourcei(sID, AL_SOURCE_STATE, &state);
if(checkALError() != AL_NO_ERROR || state == AL_STOPPED)
return false;
}
break;
}
ALuint bid;
alSourceUnqueueBuffers(sID, 1, &bid);
if(checkALError() == AL_NO_ERROR)
{
alBufferData(bid, dataFreq, dataFormat, length, readData.ptr);
alSourceQueueBuffers(sID, 1, &bid);
checkALError();
}
}
ALint state = AL_PLAYING;
alGetSourcei(sID, AL_SOURCE_STATE, &state);
if(state != AL_PLAYING) alSourcePlay(sID);
return (checkALError() == AL_NO_ERROR);
*/
ALint state;
alGetSourcei(sID, AL_SOURCE_STATE, &state);
if(checkALError() != AL_NO_ERROR || state == AL_STOPPED) return false;
if(state != AL_PLAYING) alSourcePlay(sID);
return (checkALError() == AL_NO_ERROR);
}
// Check if the music has died. This function is also used for fading.
void addTime(float time)
{
if(!musicOn) return;
if(!isPlaying()) playNext();
if(fading)
{
// Fade the volume
@ -263,14 +346,16 @@ struct MusicManager
fading = Fade.None;
volume = 0.0;
// We are done fading out, disable music.
cpp_stopSound(music);
// We are done fading out, disable music. Don't call
// enableMusic (or isPlaying) unless you want it to start
// again.
if(sID) alSourcePause(sID);
musicOn = false;
}
}
// Set the new volume
cpp_setParams(music, volume, 0.0);
if(sID) alSourcef(sID, AL_GAIN, volume);
}
}
}

@ -23,14 +23,16 @@
module sound.sfx;
import sound.audiere;
import sound.audio;
import sound.al;
import core.config;
import core.resource;
import std.string;
extern (C) ALuint alutCreateBufferFromFile(char *filename);
// Handle for a sound resource. This struct represents one sound
// effect file (not a music file, those are handled differently
// because of their size.) From this handle we may get instances,
@ -39,10 +41,10 @@ import std.string;
// file, when to kill resources, etc.
struct SoundFile
{
AudiereResource res;
ALuint bID;
char[] name;
bool loaded;
private int refs;
private void fail(char[] msg)
@ -53,36 +55,44 @@ struct SoundFile
// Load a sound resource.
void load(char[] file)
{
// Make sure the string is null terminated
assert(*(file.ptr+file.length) == 0);
name = file;
loaded = true;
refs = 0;
res = cpp_openSound(file.ptr);
if(!res) fail("Failed to open sound file " ~ file);
bID = alutCreateBufferFromFile(toStringz(file));
if(!bID) fail("Failed to open sound file " ~ file);
}
// Get an instance of this resource.
// FIXME: Should not call fail() here since it's quite possible for this to
// fail (on hardware drivers). When it does, it should check for an existing
// sound it doesn't need and kill it, then try again
SoundInstance getInstance()
{
SoundInstance si;
si.owner = this;
si.inst = cpp_createInstance(res);
if(!si.inst) fail("Failed to instantiate sound resource");
alGenSources(1, &si.inst);
if(checkALError() != AL_NO_ERROR || !si.inst)
fail("Failed to instantiate sound resource");
alSourcei(si.inst, AL_BUFFER, cast(ALint)bID);
if(checkALError() != AL_NO_ERROR)
{
alDeleteSources(1, &si.inst);
fail("Failed to load sound resource");
}
refs++;
return si;
}
// Return the sound instance when you're done with it
private void returnInstance(AudiereInstance inst)
private void returnInstance(ALuint sid)
{
refs--;
cpp_destroyInstance(inst);
alSourceStop(sid);
alDeleteSources(1, &sid);
if(refs == 0) unload();
}
@ -90,38 +100,34 @@ struct SoundFile
void unload()
{
loaded = false;
cpp_closeSound(res);
alDeleteBuffers(1, &bID);
}
}
struct SoundInstance
{
AudiereInstance inst;
ALuint inst;
SoundFile *owner;
float volume, min, max;
float xx, yy, zz; // 3D position
bool playing;
bool repeat;
// Return this instance to the owner
void kill()
{
owner.returnInstance(inst);
owner = null;
}
// Start playing a sound.
void play()
{
playing = true;
cpp_playSound(inst);
if(repeat) cpp_setRepeat(inst);
alSourcePlay(inst);
checkALError();
}
// Go buy a cookie
void stop()
{
cpp_stopSound(inst);
playing = false;
alSourceStop(inst);
checkALError();
}
// Set parameters such as max volume and range
@ -132,71 +138,35 @@ struct SoundInstance
}
body
{
this.volume = volume;
min = minRange;
max = maxRange;
this.repeat = repeat;
playing = false;
alSourcef(inst, AL_GAIN, volume);
alSourcef(inst, AL_REFERENCE_DISTANCE, minRange);
alSourcef(inst, AL_MAX_DISTANCE, maxRange);
alSourcei(inst, AL_LOOPING, repeat ? AL_TRUE : AL_FALSE);
alSourcePlay(inst);
checkALError();
}
// Set 3D position of sound
// Set 3D position of sounds. Need to convert from app's world coords to
// standard left-hand coords
void setPos(float x, float y, float z)
{
xx = x;
yy = y;
zz = z;
alSource3f(inst, AL_POSITION, x, z, -y);
checkALError();
}
// Currently VERY experimental, panning disabled. At some point we
// will likely switch to OpenAL with built-in 3D sound and dump this
// entirely.
void setPlayerPos(float x, float y, float z)
static void setPlayerPos(float x, float y, float z,
float frontx, float fronty, float frontz,
float upx, float upy, float upz)
{
//writef("{%s %s %s} ", x, y, z);
// Distance squared
x -= xx;
y -= yy;
z -= zz;
//writef("[%s %s %s] ", x, y, z);
float r2 = (x*x + y*y + z*z);
//writefln(r2, " (%s)", max*max);
// If outside range, disable
if(r2 > max*max)
{
// We just moved out of range
if(playing)
{
//writefln("Out of range");
stop();
}
}
else
{
// We just moved into range
if(!playing)
{
//writefln("In range!");
play();
}
}
if(!playing) return;
// Invert distance
if(r2 < 1) r2 = 1;
else r2 = 1/r2;
float vol = 2*r2*min*min;
float pan = 0;//80*x*r2;
//writefln("x=%s, vol=%s, pan=%s", x, vol, pan);
if(vol>1.0) vol = 1.0;
if(pan<-1.0) pan = -1.0;
else if(pan > 1.0) pan = 1.0;
//writefln("vol=", vol, " volume=", vol*volume*config.calcSfxVolume());
cpp_setParams(inst, vol*volume*config.calcSfxVolume(), pan);
ALfloat orient[6];
orient[0] = frontx;
orient[1] = frontz;
orient[2] =-fronty;
orient[3] = upx;
orient[4] = upz;
orient[5] =-upy;
alListener3f(AL_POSITION, x, z, -y);
alListenerfv(AL_ORIENTATION, orient.ptr);
checkALError();
}
}

Loading…
Cancel
Save