forked from mirror/openmw-tes3mp
First attempt at OpenAL (thanks to ChrisRobinson)
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@20 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
parent
fa6f5b81ae
commit
6988814728
21 changed files with 400 additions and 380 deletions
|
@ -37,8 +37,8 @@ Dependencies:
|
||||||
Dependencies needed to build OpenMW:
|
Dependencies needed to build OpenMW:
|
||||||
|
|
||||||
OGRE 1.4.5 (3d engine)
|
OGRE 1.4.5 (3d engine)
|
||||||
Audiere 1.9.4 (sound engine)
|
|
||||||
OIS-1.0.0 (input system)
|
OIS-1.0.0 (input system)
|
||||||
|
OpenAL (3d sound system)
|
||||||
gcc and g++ (C++ compiler)
|
gcc and g++ (C++ compiler)
|
||||||
GNU make (build tool for C++ files)
|
GNU make (build tool for C++ files)
|
||||||
DMD 1.031 (D compiler)
|
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
|
The above versions are the ones I have tested recently, but other
|
||||||
versions might work. OGRE, Audiere and OIS will require their own set
|
versions might work. OGRE and OIS will require their own set of
|
||||||
of dependencies. I recommend using an automated package tool to
|
dependencies. I recommend using an automated package tool to install
|
||||||
install as many of these as possible. On ubuntu, try typing:
|
as many of these as possible. On ubuntu, try typing:
|
||||||
|
|
||||||
sudo apt-get install libogre-dev libaudiere-dev libois-dev build-essential g++ gdc
|
sudo apt-get install libogre-dev libalut0 libois-dev build-essential g++ gdc
|
||||||
If you want to install Ogre, Audiere or OIS manually, try:
|
If you want to install Ogre, OpenAL or OIS manually, try:
|
||||||
|
|
||||||
OGRE: http://ogre3d.org
|
OGRE: http://ogre3d.org/
|
||||||
Audiere: http://audiere.sourceforge.net/
|
Audiere: http://openal.org/
|
||||||
OIS: http://sourceforge.net/projects/wgois/
|
OIS: http://sourceforge.net/projects/wgois/
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,11 +87,19 @@ installed (a D build tool), type:
|
||||||
|
|
||||||
dsss build
|
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
|
./build_openmw.sh
|
||||||
|
|
||||||
(Currently only works with the GDC compiler)
|
This build method is deprecated and only works with gdc.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
77
Makefile
77
Makefile
|
@ -3,28 +3,81 @@
|
||||||
# Compiler settings
|
# Compiler settings
|
||||||
CXX?= g++
|
CXX?= g++
|
||||||
CXXFLAGS?= -Wall -g
|
CXXFLAGS?= -Wall -g
|
||||||
|
DMD=gdmd -version=Posix
|
||||||
|
|
||||||
|
# Some extra flags for niftool and bsatool
|
||||||
|
NIFFLAGS=
|
||||||
|
|
||||||
# Compiler settings for Ogre + OIS. Change as needed.
|
# Compiler settings for Ogre + OIS. Change as needed.
|
||||||
OGCC=$(CXX) $(CXXFLAGS) `pkg-config --cflags OGRE OIS`
|
OGCC=$(CXX) $(CXXFLAGS) `pkg-config --cflags OGRE OIS openal`
|
||||||
|
|
||||||
# Compiler settings for Audiere
|
|
||||||
AGCC=$(CXX) $(CXXFLAGS) `audiere-config --cxxflags`
|
|
||||||
|
|
||||||
# Ogre C++ files, on the form ogre/cpp_X.cpp. Only the first file is
|
# Ogre C++ files, on the form ogre/cpp_X.cpp. Only the first file is
|
||||||
# passed to the compiler, the rest are dependencies.
|
# passed to the compiler, the rest are dependencies.
|
||||||
ogre_cpp=ogre framelistener interface overlay bsaarchive
|
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 ##
|
## 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
|
ogre_cpp_files=$(ogre_cpp:%=ogre/cpp_%.cpp)
|
||||||
|
|
||||||
|
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)
|
cpp_ogre.o: $(ogre_cpp_files)
|
||||||
$(OGCC) -c $<
|
$(OGCC) -c $<
|
||||||
|
|
||||||
cpp_audiere.o: $(audiere_cpp_files)
|
objs/%.o: %.d
|
||||||
$(AGCC) -c $<
|
$(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/
|
||||||
|
|
36
README.txt
36
README.txt
|
@ -5,8 +5,8 @@ Written by Nicolay Korslund
|
||||||
Email: korslund@gmail.com
|
Email: korslund@gmail.com
|
||||||
WWW: http://openmw.snaptoad.com
|
WWW: http://openmw.snaptoad.com
|
||||||
License: See GPL3.txt
|
License: See GPL3.txt
|
||||||
Current version: 0.3 (still very pre-alpha)
|
Current version: 0.4 (still very pre-alpha)
|
||||||
Date: 2008 jul. 10
|
Date: 2008 jul. 12
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,16 +18,24 @@ Morrowind installed on your system!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Release notes for 0.3
|
IMPORTANT: Subversion notes
|
||||||
=====================
|
===========================
|
||||||
|
|
||||||
As of this release, OpenMW officially builds and runs on Windows. The
|
The subversion code is currently in the process of switching from
|
||||||
installation instructions have been split into the files
|
Audiere to OpenAL. This means that:
|
||||||
COMPILE-win32.txt and README-win32.txt for the source and binary
|
|
||||||
windows releases respectively, and COMPILE-linux.txt for Linux / Unix
|
|
||||||
systems.
|
|
||||||
|
|
||||||
See the changelog at the end for more changes.
|
- 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:
|
||||||
|
|
||||||
|
- 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 an up-to-date list of changes.
|
||||||
|
|
||||||
Note: if you are using a localized (non-English) version of Morrowind,
|
Note: if you are using a localized (non-English) version of Morrowind,
|
||||||
the default starting cell (Sud) might not exist, and the esmtool
|
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.
|
- Bastien Jansen for continued testing on 64 bit linux.
|
||||||
|
|
||||||
- Bethesda Softworks for creating Morrowind!
|
- Chris Robinson for OpenAL and ALUT support
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changelog:
|
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
|
0.3 (2008 jul. 10) - latest release
|
||||||
- built and tested on Windows XP
|
- built and tested on Windows XP
|
||||||
- partial support for FreeBSD (exceptions do not work)
|
- partial support for FreeBSD (exceptions do not work)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
@echo off
|
@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 This file assumes it can find Ogre in ..\ogro
|
||||||
rem Audiere in ..\audiere
|
rem TODO: Not updated to OpenAL
|
||||||
|
|
||||||
echo Compiling C++ files
|
echo Compiling C++ files
|
||||||
g++ -c sound\cpp_audiere.cpp -I..\audiere\include
|
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
|
copy \windows\system32\d3dx9_30.dll d3dx9d_30.dll
|
||||||
|
|
||||||
echo Compiling main program (openmw.exe)
|
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
|
#!/bin/sh
|
||||||
|
|
||||||
# See INSTALL-linux.txt for instructions
|
# See COMPILE-linux.txt for instructions
|
||||||
|
|
||||||
make || exit 1
|
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]
|
[openmw.d]
|
||||||
# Add libraries and the two C++ object files
|
# 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
|
# Make sure the C++ object files are built first
|
||||||
version(Windows) {
|
version(Windows) {
|
||||||
|
@ -34,7 +34,7 @@ prebuild += dsss clean niftool
|
||||||
[esmtool.d]
|
[esmtool.d]
|
||||||
# Because of interdepencies between the ESM code and the resource
|
# Because of interdepencies between the ESM code and the resource
|
||||||
# manager, we have to include all the C++ stuff.
|
# 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
|
# Clean the nif object files to make sure they are recompiled in debug mode
|
||||||
prebuild = dsss clean niftool
|
prebuild = dsss clean niftool
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[bored.d]
|
[bored.d]
|
||||||
|
|
|
@ -87,12 +87,12 @@ struct TES3FileContext
|
||||||
{
|
{
|
||||||
char[] filename;
|
char[] filename;
|
||||||
uint leftRec, leftSub;
|
uint leftRec, leftSub;
|
||||||
size_t leftFile;
|
ulong leftFile;
|
||||||
NAME recName, subName;
|
NAME recName, subName;
|
||||||
FileType type;
|
FileType type;
|
||||||
Version ver;
|
Version ver;
|
||||||
|
|
||||||
size_t filepos;
|
ulong filepos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,7 +118,7 @@ struct TES3File
|
||||||
|
|
||||||
// These are only used by getRecHeader and getSubHeader for
|
// These are only used by getRecHeader and getSubHeader for
|
||||||
// asserting the file's integrity.
|
// 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
|
uint leftRec; // Number of unread bytes in record
|
||||||
|
|
||||||
// This is used by sub-record readers for integrity checking.
|
// 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.MarksmanThrown: writef("Thrown weapon"); break;
|
||||||
case WT.Arrow: writef("Arrow"); break;
|
case WT.Arrow: writef("Arrow"); break;
|
||||||
case WT.Bolt: writef("Bolt"); break;
|
case WT.Bolt: writef("Bolt"); break;
|
||||||
|
default: assert(0);
|
||||||
}
|
}
|
||||||
writefln(" id '%s': name '%s'", n, m.name);
|
writefln(" id '%s': name '%s'", n, m.name);
|
||||||
|
|
||||||
|
@ -278,6 +279,7 @@ void printRaw()
|
||||||
case FileType.Master: writefln("Master"); break;
|
case FileType.Master: writefln("Master"); break;
|
||||||
case FileType.Savegame: writefln("Savegame"); break;
|
case FileType.Savegame: writefln("Savegame"); break;
|
||||||
case FileType.Unknown: writefln("Unknown"); break;
|
case FileType.Unknown: writefln("Unknown"); break;
|
||||||
|
default: assert(0);
|
||||||
}
|
}
|
||||||
writef("Version: ");
|
writef("Version: ");
|
||||||
if(isVer12()) writefln("1.2");
|
if(isVer12()) writefln("1.2");
|
||||||
|
|
|
@ -309,9 +309,13 @@ extern(C) int d_frameStarted(float time)
|
||||||
if(sndCumTime > sndRefresh)
|
if(sndCumTime > sndRefresh)
|
||||||
{
|
{
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
cpp_getCameraPos(&x, &y, &z);
|
float fx, fy, fz;
|
||||||
|
float ux, uy, uz;
|
||||||
|
|
||||||
soundScene.update(x,y,z);
|
cpp_getCameraPos(&x, &y, &z);
|
||||||
|
cpp_getCameraOrientation(&fx, &fy, &fz, &ux, &uy, &uz);
|
||||||
|
|
||||||
|
soundScene.update(x,y,z,fx,fy,fz,ux,uy,uz);
|
||||||
sndCumTime -= sndRefresh;
|
sndCumTime -= sndRefresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,7 @@ class NiAutoNormalParticlesData : ShapeData
|
||||||
debug(verbose) writef("Active vertices: ");
|
debug(verbose) writef("Active vertices: ");
|
||||||
|
|
||||||
// Number of active vertices (always matches count?)
|
// 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.wgetFloat(); // Active radius (?)
|
||||||
nifFile.wgetShort(); // Number of valid entries in the following arrays
|
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
|
// Save a screen shot to the given file name
|
||||||
void cpp_screenshot(char *filename);
|
void cpp_screenshot(char *filename);
|
||||||
|
|
||||||
// Camera controll
|
// Camera control and information
|
||||||
void cpp_rotateCamera(float x, float y);
|
void cpp_rotateCamera(float x, float y);
|
||||||
void cpp_moveCamera(float x, float y, float z, float r1, float r2, float r3);
|
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_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);
|
void cpp_moveCameraRel(float x, float y, float z);
|
||||||
|
|
||||||
// Do some debug action. Check the menu for today's specials!
|
// 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];
|
*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
|
// Move and rotate camera in place. Transforms Morrowind coordinates
|
||||||
// to OGRE coordinates.
|
// to OGRE coordinates.
|
||||||
extern "C" void cpp_moveCamera(float x, float y, float z,
|
extern "C" void cpp_moveCamera(float x, float y, float z,
|
||||||
|
|
6
openmw.d
6
openmw.d
|
@ -359,9 +359,15 @@ void main(char[][] args)
|
||||||
|
|
||||||
// Run it until the user tells us to quit
|
// Run it until the user tells us to quit
|
||||||
startRendering();
|
startRendering();
|
||||||
|
|
||||||
|
jukebox.disableMusic();
|
||||||
|
battleMusic.disableMusic();
|
||||||
}
|
}
|
||||||
else debug(verbose) writefln("Skipping rendering");
|
else debug(verbose) writefln("Skipping rendering");
|
||||||
|
|
||||||
|
soundScene.kill();
|
||||||
|
shutdownSound();
|
||||||
|
|
||||||
debug(verbose)
|
debug(verbose)
|
||||||
{
|
{
|
||||||
writefln();
|
writefln();
|
||||||
|
|
|
@ -37,10 +37,7 @@ SoundList soundScene;
|
||||||
// very long.
|
// very long.
|
||||||
struct SoundList
|
struct SoundList
|
||||||
{
|
{
|
||||||
// TODO: This is really just a test, a hack. Will be replaced by a
|
SoundInstance[] list;
|
||||||
// list or similar later.
|
|
||||||
SoundInstance list[50];
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
// Get a sound instance from a Sound struct
|
// Get a sound instance from a Sound struct
|
||||||
static SoundInstance getInstance(Sound *s, bool loop=false)
|
static SoundInstance getInstance(Sound *s, bool loop=false)
|
||||||
|
@ -57,16 +54,33 @@ struct SoundList
|
||||||
|
|
||||||
SoundInstance *insert(Sound *snd, bool loop=false)
|
SoundInstance *insert(Sound *snd, bool loop=false)
|
||||||
{
|
{
|
||||||
if(index == 50) return null;
|
// Reuse a dead instance if one exists
|
||||||
|
foreach(ref s; list)
|
||||||
SoundInstance *s = &list[index++];
|
{
|
||||||
*s = getInstance(snd, loop);
|
if(s.owner == null)
|
||||||
return s;
|
{
|
||||||
|
s = getInstance(snd, loop);
|
||||||
|
return &s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise append a new one
|
||||||
|
list ~= getInstance(snd, loop);
|
||||||
|
return &list[$-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(float x, float y, float z)
|
void update(float x, float y, float z,
|
||||||
|
float frontx, float fronty, float frontz,
|
||||||
|
float upx, float upy, float upz)
|
||||||
{
|
{
|
||||||
foreach(ref s; list[0..index])
|
SoundInstance.setPlayerPos(x,y,z,frontx,fronty,frontz,upx,upy,upz);
|
||||||
s.setPlayerPos(x,y,z);
|
}
|
||||||
|
|
||||||
|
void kill()
|
||||||
|
{
|
||||||
|
foreach(ref s; list)
|
||||||
|
{
|
||||||
|
if(s.owner) s.kill();
|
||||||
|
}
|
||||||
|
list = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
module sound.al;
|
||||||
|
|
||||||
extern(System):
|
extern(System):
|
||||||
|
|
||||||
//Defines
|
//Defines
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
module sound.alc;
|
||||||
|
|
||||||
extern(System):
|
extern(System):
|
||||||
|
|
||||||
//Definitions
|
//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.sfx;
|
||||||
public import sound.music;
|
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
|
class SoundException : Exception
|
||||||
{
|
{
|
||||||
|
@ -38,9 +46,40 @@ MusicManager battleMusic;
|
||||||
|
|
||||||
void initializeSound()
|
void initializeSound()
|
||||||
{
|
{
|
||||||
if(cpp_openDevice())
|
Device = alcOpenDevice(null);
|
||||||
|
Context = alcCreateContext(Device, null);
|
||||||
|
|
||||||
|
if(!Device || !Context)
|
||||||
throw new SoundException("initializeSound()",
|
throw new SoundException("initializeSound()",
|
||||||
"Failed to initialize music device");
|
"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");
|
jukebox.initialize("Main");
|
||||||
battleMusic.initialize("Battle");
|
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();
|
|
||||||
}
|
|
167
sound/music.d
167
sound/music.d
|
@ -23,34 +23,29 @@
|
||||||
|
|
||||||
module sound.music;
|
module sound.music;
|
||||||
|
|
||||||
import sound.audiere;
|
|
||||||
import sound.audio;
|
import sound.audio;
|
||||||
|
import sound.al;
|
||||||
|
|
||||||
import std.string;
|
import std.string;
|
||||||
|
|
||||||
import core.config;
|
import core.config;
|
||||||
import core.resource;
|
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.
|
// Simple music player, has a playlist and can pause/resume music.
|
||||||
struct MusicManager
|
struct MusicManager
|
||||||
{
|
{
|
||||||
private:
|
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
|
// How much to add to the volume each second when fading
|
||||||
const float fadeInRate = 0.10;
|
const float fadeInRate = 0.10;
|
||||||
const float fadeOutRate = 0.35;
|
const float fadeOutRate = 0.35;
|
||||||
|
|
||||||
// Volume
|
// Volume
|
||||||
float volume, maxVolume;
|
ALfloat volume, maxVolume;
|
||||||
|
|
||||||
// Time since last time we polled
|
|
||||||
float sumTime;
|
|
||||||
|
|
||||||
char[] name;
|
char[] name;
|
||||||
|
|
||||||
|
@ -73,7 +68,10 @@ struct MusicManager
|
||||||
int index; // Index of next song to play
|
int index; // Index of next song to play
|
||||||
|
|
||||||
bool musicOn;
|
bool musicOn;
|
||||||
AudiereInstance music;
|
ALuint sID;
|
||||||
|
ALuint bIDs[1];
|
||||||
|
|
||||||
|
ubyte[] readData;
|
||||||
|
|
||||||
// Which direction are we currently fading, if any
|
// Which direction are we currently fading, if any
|
||||||
enum Fade { None = 0, In, Out }
|
enum Fade { None = 0, In, Out }
|
||||||
|
@ -85,6 +83,8 @@ struct MusicManager
|
||||||
void initialize(char[] name)
|
void initialize(char[] name)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
sID = 0;
|
||||||
|
foreach(ref b; bIDs) b = 0;
|
||||||
musicOn = false;
|
musicOn = false;
|
||||||
updateVolume();
|
updateVolume();
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ struct MusicManager
|
||||||
// a fade. Even if we are fading, though, the volume should never
|
// a fade. Even if we are fading, though, the volume should never
|
||||||
// be over the max.
|
// be over the max.
|
||||||
if(fading == Fade.None || volume > maxVolume) volume = maxVolume;
|
if(fading == Fade.None || volume > maxVolume) volume = maxVolume;
|
||||||
cpp_setParams(music, volume, 0.0);
|
alSourcef(sID, AL_GAIN, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give a music play list
|
// Give a music play list
|
||||||
|
@ -120,7 +120,8 @@ struct MusicManager
|
||||||
{
|
{
|
||||||
if(playlist.length < 2) return;
|
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;
|
int left = playlist.length;
|
||||||
rndList.length = left;
|
rndList.length = left;
|
||||||
|
@ -166,13 +167,25 @@ struct MusicManager
|
||||||
// If music is disabled, do nothing
|
// If music is disabled, do nothing
|
||||||
if(!musicOn) return;
|
if(!musicOn) return;
|
||||||
|
|
||||||
// Kill current track
|
|
||||||
if(music) cpp_destroyInstance(music);
|
|
||||||
music = null;
|
|
||||||
|
|
||||||
// No tracks to play?
|
// No tracks to play?
|
||||||
if(!playlist.length) return;
|
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
|
// End of list? Randomize and start over
|
||||||
if(index == playlist.length)
|
if(index == playlist.length)
|
||||||
{
|
{
|
||||||
|
@ -180,9 +193,35 @@ struct MusicManager
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
music = cpp_playStream(toStringz(playlist[index]), volume);
|
readData.length = 128*1024 / bIDs.length;
|
||||||
|
|
||||||
if(!music) fail("Unable to start music track " ~ playlist[index]);
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -192,7 +231,6 @@ struct MusicManager
|
||||||
{
|
{
|
||||||
if(!config.useMusic) return;
|
if(!config.useMusic) return;
|
||||||
|
|
||||||
sumTime = 0;
|
|
||||||
musicOn = true;
|
musicOn = true;
|
||||||
volume = maxVolume;
|
volume = maxVolume;
|
||||||
fading = Fade.None;
|
fading = Fade.None;
|
||||||
|
@ -202,9 +240,16 @@ struct MusicManager
|
||||||
// Disable music
|
// Disable music
|
||||||
void disableMusic()
|
void disableMusic()
|
||||||
{
|
{
|
||||||
if(music) cpp_destroyInstance(music);
|
if(sID)
|
||||||
music = null;
|
{
|
||||||
musicOn = false;
|
alSourceStop(sID);
|
||||||
|
alDeleteSources(1, &sID);
|
||||||
|
alDeleteBuffers(bIDs.length, bIDs.ptr);
|
||||||
|
checkALError();
|
||||||
|
sID = 0;
|
||||||
|
foreach(ref b; bIDs) b = 0;
|
||||||
|
}
|
||||||
|
musicOn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause current track
|
// Pause current track
|
||||||
|
@ -218,29 +263,67 @@ struct MusicManager
|
||||||
{
|
{
|
||||||
if(!config.useMusic) return;
|
if(!config.useMusic) return;
|
||||||
|
|
||||||
sumTime = 0;
|
|
||||||
volume = 0.0;
|
volume = 0.0;
|
||||||
fading = Fade.In;
|
fading = Fade.In;
|
||||||
musicOn = true;
|
musicOn = true;
|
||||||
if(music) cpp_playSound(music);
|
if(sID) addTime(0);
|
||||||
else playNext();
|
else playNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add time since last frame to the counter. If the accumulated time
|
// Checks if a stream is playing, filling more data as needed, and restarting
|
||||||
// has passed the polling interval, then check if the music has
|
// if it stalled or was paused.
|
||||||
// died. The Audiere library has a callback functionality, but it
|
private bool isPlaying()
|
||||||
// turned out not to be terribly reliable. Sometimes it was never
|
{
|
||||||
// called at all. So we have to poll at regular intervals .. :( This
|
if(!sID) return false;
|
||||||
// function is also used for fading.
|
/* 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++)
|
||||||
|
{
|
||||||
|
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)
|
void addTime(float time)
|
||||||
{
|
{
|
||||||
if(!musicOn) return;
|
if(!musicOn) return;
|
||||||
sumTime += time;
|
|
||||||
if(sumTime > pollInterval)
|
if(!isPlaying()) playNext();
|
||||||
{
|
|
||||||
sumTime = 0;
|
|
||||||
if(!cpp_isPlaying(music)) playNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fading)
|
if(fading)
|
||||||
{
|
{
|
||||||
|
@ -263,14 +346,16 @@ struct MusicManager
|
||||||
fading = Fade.None;
|
fading = Fade.None;
|
||||||
volume = 0.0;
|
volume = 0.0;
|
||||||
|
|
||||||
// We are done fading out, disable music.
|
// We are done fading out, disable music. Don't call
|
||||||
cpp_stopSound(music);
|
// enableMusic (or isPlaying) unless you want it to start
|
||||||
|
// again.
|
||||||
|
if(sID) alSourcePause(sID);
|
||||||
musicOn = false;
|
musicOn = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the new volume
|
// Set the new volume
|
||||||
cpp_setParams(music, volume, 0.0);
|
if(sID) alSourcef(sID, AL_GAIN, volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
136
sound/sfx.d
136
sound/sfx.d
|
@ -23,14 +23,16 @@
|
||||||
|
|
||||||
module sound.sfx;
|
module sound.sfx;
|
||||||
|
|
||||||
import sound.audiere;
|
|
||||||
import sound.audio;
|
import sound.audio;
|
||||||
|
import sound.al;
|
||||||
|
|
||||||
import core.config;
|
import core.config;
|
||||||
import core.resource;
|
import core.resource;
|
||||||
|
|
||||||
import std.string;
|
import std.string;
|
||||||
|
|
||||||
|
extern (C) ALuint alutCreateBufferFromFile(char *filename);
|
||||||
|
|
||||||
// Handle for a sound resource. This struct represents one sound
|
// Handle for a sound resource. This struct represents one sound
|
||||||
// effect file (not a music file, those are handled differently
|
// effect file (not a music file, those are handled differently
|
||||||
// because of their size.) From this handle we may get instances,
|
// because of their size.) From this handle we may get instances,
|
||||||
|
@ -39,10 +41,10 @@ import std.string;
|
||||||
// file, when to kill resources, etc.
|
// file, when to kill resources, etc.
|
||||||
struct SoundFile
|
struct SoundFile
|
||||||
{
|
{
|
||||||
AudiereResource res;
|
ALuint bID;
|
||||||
char[] name;
|
char[] name;
|
||||||
bool loaded;
|
bool loaded;
|
||||||
|
|
||||||
private int refs;
|
private int refs;
|
||||||
|
|
||||||
private void fail(char[] msg)
|
private void fail(char[] msg)
|
||||||
|
@ -53,36 +55,44 @@ struct SoundFile
|
||||||
// Load a sound resource.
|
// Load a sound resource.
|
||||||
void load(char[] file)
|
void load(char[] file)
|
||||||
{
|
{
|
||||||
// Make sure the string is null terminated
|
|
||||||
assert(*(file.ptr+file.length) == 0);
|
|
||||||
|
|
||||||
name = file;
|
name = file;
|
||||||
|
|
||||||
loaded = true;
|
loaded = true;
|
||||||
refs = 0;
|
refs = 0;
|
||||||
|
|
||||||
res = cpp_openSound(file.ptr);
|
bID = alutCreateBufferFromFile(toStringz(file));
|
||||||
|
if(!bID) fail("Failed to open sound file " ~ file);
|
||||||
if(!res) fail("Failed to open sound file " ~ file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an instance of this resource.
|
// 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 getInstance()
|
||||||
{
|
{
|
||||||
SoundInstance si;
|
SoundInstance si;
|
||||||
si.owner = this;
|
si.owner = this;
|
||||||
si.inst = cpp_createInstance(res);
|
alGenSources(1, &si.inst);
|
||||||
if(!si.inst) fail("Failed to instantiate sound resource");
|
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++;
|
refs++;
|
||||||
|
|
||||||
return si;
|
return si;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the sound instance when you're done with it
|
// Return the sound instance when you're done with it
|
||||||
private void returnInstance(AudiereInstance inst)
|
private void returnInstance(ALuint sid)
|
||||||
{
|
{
|
||||||
refs--;
|
refs--;
|
||||||
cpp_destroyInstance(inst);
|
alSourceStop(sid);
|
||||||
|
alDeleteSources(1, &sid);
|
||||||
if(refs == 0) unload();
|
if(refs == 0) unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,38 +100,34 @@ struct SoundFile
|
||||||
void unload()
|
void unload()
|
||||||
{
|
{
|
||||||
loaded = false;
|
loaded = false;
|
||||||
cpp_closeSound(res);
|
alDeleteBuffers(1, &bID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SoundInstance
|
struct SoundInstance
|
||||||
{
|
{
|
||||||
AudiereInstance inst;
|
ALuint inst;
|
||||||
SoundFile *owner;
|
SoundFile *owner;
|
||||||
float volume, min, max;
|
|
||||||
float xx, yy, zz; // 3D position
|
|
||||||
bool playing;
|
|
||||||
bool repeat;
|
|
||||||
|
|
||||||
// Return this instance to the owner
|
// Return this instance to the owner
|
||||||
void kill()
|
void kill()
|
||||||
{
|
{
|
||||||
owner.returnInstance(inst);
|
owner.returnInstance(inst);
|
||||||
|
owner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start playing a sound.
|
// Start playing a sound.
|
||||||
void play()
|
void play()
|
||||||
{
|
{
|
||||||
playing = true;
|
alSourcePlay(inst);
|
||||||
cpp_playSound(inst);
|
checkALError();
|
||||||
if(repeat) cpp_setRepeat(inst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go buy a cookie
|
// Go buy a cookie
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
cpp_stopSound(inst);
|
alSourceStop(inst);
|
||||||
playing = false;
|
checkALError();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set parameters such as max volume and range
|
// Set parameters such as max volume and range
|
||||||
|
@ -132,71 +138,35 @@ struct SoundInstance
|
||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
this.volume = volume;
|
alSourcef(inst, AL_GAIN, volume);
|
||||||
min = minRange;
|
alSourcef(inst, AL_REFERENCE_DISTANCE, minRange);
|
||||||
max = maxRange;
|
alSourcef(inst, AL_MAX_DISTANCE, maxRange);
|
||||||
this.repeat = repeat;
|
alSourcei(inst, AL_LOOPING, repeat ? AL_TRUE : AL_FALSE);
|
||||||
playing = 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)
|
void setPos(float x, float y, float z)
|
||||||
{
|
{
|
||||||
xx = x;
|
alSource3f(inst, AL_POSITION, x, z, -y);
|
||||||
yy = y;
|
checkALError();
|
||||||
zz = z;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently VERY experimental, panning disabled. At some point we
|
static void setPlayerPos(float x, float y, float z,
|
||||||
// will likely switch to OpenAL with built-in 3D sound and dump this
|
float frontx, float fronty, float frontz,
|
||||||
// entirely.
|
float upx, float upy, float upz)
|
||||||
void setPlayerPos(float x, float y, float z)
|
|
||||||
{
|
{
|
||||||
//writef("{%s %s %s} ", x, y, z);
|
ALfloat orient[6];
|
||||||
// Distance squared
|
orient[0] = frontx;
|
||||||
x -= xx;
|
orient[1] = frontz;
|
||||||
y -= yy;
|
orient[2] =-fronty;
|
||||||
z -= zz;
|
orient[3] = upx;
|
||||||
//writef("[%s %s %s] ", x, y, z);
|
orient[4] = upz;
|
||||||
float r2 = (x*x + y*y + z*z);
|
orient[5] =-upy;
|
||||||
//writefln(r2, " (%s)", max*max);
|
alListener3f(AL_POSITION, x, z, -y);
|
||||||
|
alListenerfv(AL_ORIENTATION, orient.ptr);
|
||||||
// If outside range, disable
|
checkALError();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue