1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-24 00:39:49 +00:00

Music jukebox is now handled by Monster!

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@64 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
nkorslund 2008-11-08 23:42:12 +00:00
parent d1ea2acd93
commit 54f598d2e7
10 changed files with 449 additions and 253 deletions

View File

@ -81,15 +81,15 @@ void toggleBattle()
if(battle) if(battle)
{ {
writefln("Changing to normal music"); writefln("Changing to normal music");
jukebox.resumeMusic(); jukebox.resume();
battleMusic.pauseMusic(); battleMusic.pause();
battle=false; battle=false;
} }
else else
{ {
writefln("Changing to battle music"); writefln("Changing to battle music");
jukebox.pauseMusic(); jukebox.pause();
battleMusic.resumeMusic(); battleMusic.resume();
battle=true; battle=true;
} }
} }
@ -298,8 +298,8 @@ extern(C) int d_frameStarted(float time)
musCumTime += time; musCumTime += time;
if(musCumTime > musRefresh) if(musCumTime > musRefresh)
{ {
jukebox.addTime(musRefresh); jukebox.updateBuffers();
battleMusic.addTime(musRefresh); battleMusic.updateBuffers();
musCumTime -= musRefresh; musCumTime -= musRefresh;
} }

View File

@ -30,6 +30,7 @@ import monster.vm.error;
import std.string; import std.string;
import std.uni; import std.uni;
import std.stdio; import std.stdio;
import std.utf;
// An index to an array. Array indices may be 0, unlike object indices // An index to an array. Array indices may be 0, unlike object indices
// which span from 1 and upwards, and has 0 as the illegal 'null' // which span from 1 and upwards, and has 0 as the illegal 'null'
@ -152,6 +153,9 @@ struct Arrays
alias createT!(dchar) create; alias createT!(dchar) create;
alias createT!(AIndex) create; alias createT!(AIndex) create;
ArrayRef *create(char[] arg)
{ return create(toUTF32(arg)); }
// Generic element size // Generic element size
ArrayRef *create(int[] data, int size) ArrayRef *create(int[] data, int size)
{ {

View File

@ -99,7 +99,7 @@ struct CodeStack
fleft = 0; fleft = 0;
return; return;
} }
fleft = left + (frm-pos); fleft = left + (pos-frm);
assert(fleft >= 0 && fleft <= total); assert(fleft >= 0 && fleft <= total);
} }

212
mscripts/jukebox.mn Normal file
View File

@ -0,0 +1,212 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (jukebox.mn) 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/ .
*/
/*
A simple jukebox with a playlist. It can play, stop, pause with
fade in and fade out, and adjust volume.
*/
class Jukebox : Object;
// Between 0 (off) and 1 (full volume)
float fadeLevel = 0.0;
// How much to fade in and out each second
float fadeInRate = 0.10;
float fadeOutRate = 0.25;
// Time between each fade step
float fadeInterval = 0.2;
// List of sounds to play
char[][] playlist;
// Index of current song
int index;
// The music volume, set by the user. Does NOT change to adjust for
// fading, etc. TODO: This should be stored in a configuration class,
// not here.
float musVolume;
bool isPlaying; // Is a song currently playing?
// TODO: Make "isPaused" instead, makes more sense
bool hasSong; // Is a song currently selected (playing or paused)
// TODO: Move to Object for now
native int randInt(int a, int b);
// Native functions to control music
native setSound(char[] filename);
native setVolume(float f);
native playSound();
native stopSound();
idle waitUntilFinished();
// Fade out and then stop the music. TODO: Rename these to resume()
// etc and use super.resume, when this is possible.
pause() { state = fadeOut; }
resume()
{
if(!hasSong) next();
else playSound();
state = fadeIn;
}
// Stop the current song. Calling resume again after this is called
// will start a new song.
stop()
{
stopSound();
hasSong = false;
isPlaying = false;
fadeLevel = 0.0;
state = null;
}
play()
{
if(index >= playlist.length)
return;
setSound(playlist[index]);
playSound();
isPlaying = true;
hasSong = true;
}
// Play the next song in the playlist
next()
{
if(isPlaying)
stop();
// Find the index of the next song, if any
if(playlist.length == 0) return;
if(++index >= playlist.length)
{
index = 0;
randomize();
}
play();
}
// Set the new music volume setting. TODO: This should be read from a
// config object instead.
updateVolume(float vol)
{
musVolume = vol;
if(isPlaying)
setVolume(musVolume*fadeLevel);
}
setPlaylist(char[][] lst)
{
playlist = lst;
randomize();
}
// Randomize playlist.
randomize()
{
if(playlist.length < 2) return;
foreach(int i, char[] s; playlist)
{
// Index to switch with
int idx = randInt(i,playlist.length-1);
// To avoid playing the same song twice in a row, don't set the
// first song to the previous last.
if(i == 0 && idx == playlist.length-1)
idx--;
if(idx == i) // Skip if swapping with self
continue;
playlist[i] = playlist[idx];
playlist[idx] = s;
}
}
// Fade in
state fadeIn
{
begin:
setVolume(musVolume*fadeLevel);
sleep(fadeInterval);
fadeLevel += fadeInterval*fadeInRate;
if(fadeLevel >= 1.0)
{
fadeLevel = 1.0;
setVolume(musVolume);
state = playing;
}
goto begin;
}
// Fade out
state fadeOut
{
begin:
sleep(fadeInterval);
fadeLevel -= fadeInterval*fadeOutRate;
if(fadeLevel <= 0.0)
{
fadeLevel = 0.0;
stopSound();
isPlaying = false;
state = null;
}
setVolume(musVolume*fadeLevel);
goto begin;
}
state playing
{
begin:
// Wait for the song to play. Will return imediately if the song has
// already stopped or if no song is playing
waitUntilFinished();
// Start playing the next song
next();
goto begin;
}

View File

@ -1,3 +1,26 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (object.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/ .
*/
module mscripts.object; module mscripts.object;
import monster.monster; import monster.monster;

View File

@ -1,3 +1,26 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (object.mn) 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 is the base class of all OpenMW Monster classes. // This is the base class of all OpenMW Monster classes.
class Object; class Object;

View File

@ -347,7 +347,7 @@ void main(char[][] args)
initializeInput(); initializeInput();
// Start swangin' // Start swangin'
if(!noSound) jukebox.enableMusic(); if(!noSound) jukebox.play();
// Run it until the user tells us to quit // Run it until the user tells us to quit
startRendering(); startRendering();

View File

@ -26,8 +26,6 @@ module sound.audio;
public import sound.sfx; public import sound.sfx;
public import sound.music; public import sound.music;
import monster.monster;
import sound.al; import sound.al;
import sound.alc; import sound.alc;
@ -56,14 +54,16 @@ void initializeSound()
alcMakeContextCurrent(Context); alcMakeContextCurrent(Context);
MusicManager.sinit();
jukebox.initialize("Main"); jukebox.initialize("Main");
battleMusic.initialize("Battle"); battleMusic.initialize("Battle");
} }
void shutdownSound() void shutdownSound()
{ {
jukebox.disableMusic(); jukebox.shutdown();
battleMusic.disableMusic(); battleMusic.shutdown();
alcMakeContextCurrent(null); alcMakeContextCurrent(null);
if(Context) alcDestroyContext(Context); if(Context) alcDestroyContext(Context);
@ -72,14 +72,14 @@ void shutdownSound()
Device = null; Device = null;
} }
bool checkALError(char[] what = "") void checkALError(char[] what = "")
{ {
ALenum err = alGetError(); ALenum err = alGetError();
if(what.length) what = " while " ~ what; if(what.length) what = " while " ~ what;
if(err != AL_NO_ERROR) if(err != AL_NO_ERROR)
writefln("WARNING: OpenAL error%s: (%x) %s", what, err, throw new Exception(format("OpenAL error%s: (%x) %s", what, err,
toString(alGetString(err))); toString(alGetString(err))));
return err != AL_NO_ERROR;
} }
bool noALError()
{ return alGetError() == AL_NO_ERROR; }

View File

@ -27,27 +27,36 @@ import sound.avcodec;
import sound.audio; import sound.audio;
import sound.al; import sound.al;
import monster.monster;
import std.stdio; import std.stdio;
import std.string; import std.string;
import core.config; import core.config;
import core.resource; import core.resource;
class Idle_waitUntilFinished : IdleFunction
{
override:
bool initiate(MonsterObject *mo) { return true; }
bool hasFinished(MonsterObject *mo)
{
MusicManager *mgr = cast(MusicManager*)mo.extra;
// Return when the music is no longer playing
return !mgr.isPlaying();
}
}
// 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 much to add to the volume each second when fading
const float fadeInRate = 0.10;
const float fadeOutRate = 0.20;
// Maximum buffer length, divided up among OpenAL buffers // Maximum buffer length, divided up among OpenAL buffers
const uint bufLength = 128*1024; const uint bufLength = 128*1024;
// Volume
ALfloat volume, maxVolume;
char[] name; char[] name;
void fail(char[] msg) void fail(char[] msg)
@ -55,115 +64,122 @@ struct MusicManager
throw new SoundException(name ~ " Jukebox", msg); throw new SoundException(name ~ " Jukebox", msg);
} }
// List of songs to play ALuint sID; // Sound id
char[][] playlist; ALuint bIDs[4]; // Buffers
int index; // Index of next song to play
bool musicOn;
ALuint sID;
ALuint bIDs[4];
ALenum bufFormat; ALenum bufFormat;
ALint bufRate; ALint bufRate;
AVFile fileHandle; AVFile fileHandle;
AVAudio audioHandle; AVAudio audioHandle;
ubyte[] outData;
// Which direction are we currently fading, if any static ubyte[] outData;
enum Fade { None = 0, In, Out }
Fade fading; // The Jukebox class
static MonsterClass mc;
// The jukebox Monster object
MonsterObject *mo;
public: public:
static MusicManager *get()
{ return cast(MusicManager*)params.obj().extra; }
static void sinit()
{
assert(mc is null);
mc = new MonsterClass("Jukebox", "jukebox.mn");
mc.bind("randInt",
{ stack.pushInt(rnd.randInt
(stack.popInt,stack.popInt));});
mc.bind("waitUntilFinished",
new Idle_waitUntilFinished);
mc.bind("setSound", { get().setSound(); });
mc.bind("setVolume", { get().setVolume(); });
mc.bind("playSound", { get().playSound(); });
mc.bind("stopSound", { get().stopSound(); });
outData.length = bufLength / bIDs.length;
}
// Initialize the jukebox // Initialize the jukebox
void initialize(char[] name) void initialize(char[] name)
{ {
this.name = name; this.name = name;
sID = 0; sID = 0;
foreach(ref b; bIDs) b = 0; bIDs[] = 0;
outData.length = bufLength / bIDs.length;
fileHandle = null; fileHandle = null;
musicOn = false;
mo = mc.createObject();
mo.extra = this;
} }
// Get the new volume setting. // Called whenever the volume configuration values are changed by
// the user.
void updateVolume() void updateVolume()
{ {
maxVolume = config.calcMusicVolume(); stack.pushFloat(config.calcMusicVolume());
mo.call("updateVolume");
if(!musicOn) return;
// Adjust volume up to new setting, unless we are in the middle of
// a fade. Even if we are fading, though, the volume should never
// be over the max.
if(fading == Fade.None || volume > maxVolume) volume = maxVolume;
if(sID)
alSourcef(sID, AL_GAIN, volume);
} }
// Give a music play list // Give a music play list
void setPlaylist(char[][] pl) void setPlaylist(char[][] pl)
{ {
playlist = pl; AIndex arr[];
index = 0; arr.length = pl.length;
randomize(); // Create the array indices for each element string
foreach(i, ref elm; arr)
elm = arrays.create(pl[i]).getIndex();
// Push the final array
stack.pushArray(arr);
mo.call("setPlaylist");
} }
// Randomize playlist. If the argument is true, then we don't want // Pause current track
// the old last to be the new first. void pause() { mo.call("pause"); }
private void randomize(bool checklast = false)
{
if(playlist.length < 2) return;
// Get the index of the last song played // Resume. Starts playing sound, with fade in
int lastidx = ((index==0) ? (playlist.length-1) : (index-1)); void resume()
foreach(int i, char[] s; playlist)
{ {
int idx = rnd.randInt(i,playlist.length-1); if(!config.useMusic) return;
mo.call("resume");
// Don't put the last idx as the first entry
if(i == 0 && checklast && lastidx == idx)
{
idx++;
if(idx == playlist.length)
idx = i;
}
if(idx == i) /* skip if swapping with self */
continue;
playlist[i] = playlist[idx];
playlist[idx] = s;
}
} }
// Skip to the next track void play()
void playNext()
{ {
// If music is disabled, do nothing if(!config.useMusic) return;
if(!musicOn) return; mo.call("play");
}
// No tracks to play? void setSound()
if(!playlist.length) return; {
char[] fname = stack.popString8();
// Generate a source to play back with if needed // Generate a source to play back with if needed
if(!sID) if(!sID)
{ {
alGenSources(1, &sID); alGenSources(1, &sID);
if(checkALError()) checkALError("generating buffers");
fail("Couldn't generate music sources");
// Set listner relative coordinates (sound follows the player)
alSourcei(sID, AL_SOURCE_RELATIVE, AL_TRUE); alSourcei(sID, AL_SOURCE_RELATIVE, AL_TRUE);
alGenBuffers(bIDs.length, bIDs.ptr);
updateVolume(); updateVolume();
} }
else else
{ {
// Kill current track // Kill current track, but keep the sID source.
alSourceStop(sID); alSourceStop(sID);
alSourcei(sID, AL_BUFFER, 0); alSourcei(sID, AL_BUFFER, 0);
alDeleteBuffers(bIDs.length, bIDs.ptr); //alDeleteBuffers(bIDs.length, bIDs.ptr);
bIDs[] = 0; //bIDs[] = 0;
checkALError("killing current track"); checkALError("killing current track");
} }
@ -171,57 +187,30 @@ struct MusicManager
fileHandle = null; fileHandle = null;
audioHandle = null; audioHandle = null;
// End of list? Randomize and start over //alGenBuffers(bIDs.length, bIDs.ptr);
if(index == playlist.length)
{
randomize(true);
index = 0;
}
alGenBuffers(bIDs.length, bIDs.ptr);
// If something fails, clean everything up. // If something fails, clean everything up.
scope(failure) scope(failure) shutdown();
{
// This block is only executed if an exception is thrown.
if(fileHandle) avc_closeAVFile(fileHandle); fileHandle = avc_openAVFile(toStringz(fname));
fileHandle = null;
audioHandle = null;
alSourceStop(sID);
alDeleteSources(1, &sID);
alDeleteBuffers(bIDs.length, bIDs.ptr);
checkALError("cleaning up after music failure");
sID = 0;
bIDs[] = 0;
// Try the next track if playNext is called again
index++;
// The function exits here.
}
if(checkALError())
fail("Couldn't generate buffers");
fileHandle = avc_openAVFile(toStringz(playlist[index]));
if(!fileHandle) if(!fileHandle)
fail("Unable to open " ~ playlist[index]); fail("Unable to open " ~ fname);
audioHandle = avc_getAVAudioStream(fileHandle, 0); audioHandle = avc_getAVAudioStream(fileHandle, 0);
if(!audioHandle) if(!audioHandle)
fail("Unable to load music track " ~ playlist[index]); fail("Unable to load music track " ~ fname);
int ch, bits, rate; int rate, ch, bits;
if(avc_getAVAudioInfo(audioHandle, &rate, &ch, &bits) != 0) if(avc_getAVAudioInfo(audioHandle, &rate, &ch, &bits) != 0)
fail("Unable to get info for music track " ~ playlist[index]); fail("Unable to get info for music track " ~ fname);
// Translate format from avformat to OpenAL
bufRate = rate; bufRate = rate;
bufFormat = 0; bufFormat = 0;
// TODO: These don't really fail gracefully for 4 and 6 channels
// if these aren't supported.
if(bits == 8) if(bits == 8)
{ {
if(ch == 1) bufFormat = AL_FORMAT_MONO8; if(ch == 1) bufFormat = AL_FORMAT_MONO8;
@ -244,16 +233,17 @@ struct MusicManager
} }
if(bufFormat == 0) if(bufFormat == 0)
fail(format("Unhandled format (%d channels, %d bits) for music track %s", ch, bits, playlist[index])); fail(format("Unhandled format (%d channels, %d bits) for music track %s", ch, bits, fname));
// Fill the buffers
foreach(int i, ref b; bIDs) foreach(int i, ref b; bIDs)
{ {
int length = avc_getAVAudioData(audioHandle, outData.ptr, outData.length); int length = avc_getAVAudioData(audioHandle, outData.ptr, outData.length);
if(length) alBufferData(b, bufFormat, outData.ptr, length, bufRate); if(length) alBufferData(b, bufFormat, outData.ptr, length, bufRate);
if(length == 0 || checkALError()) if(length == 0 || !noALError())
{ {
if(i == 0) if(i == 0)
fail("No audio data in music track " ~ playlist[index]); fail("No audio data in music track " ~ fname);
alDeleteBuffers(bIDs.length-i, bIDs.ptr+i); alDeleteBuffers(bIDs.length-i, bIDs.ptr+i);
checkALError("running alDeleteBuffers"); checkALError("running alDeleteBuffers");
@ -262,28 +252,74 @@ struct MusicManager
} }
} }
// Associate the buffers with the sound id
alSourceQueueBuffers(sID, bIDs.length, bIDs.ptr); alSourceQueueBuffers(sID, bIDs.length, bIDs.ptr);
alSourcePlay(sID);
if(checkALError())
fail("Unable to start music track " ~ playlist[index]);
index++;
return;
} }
// Start playing the jukebox void setVolume()
void enableMusic()
{ {
if(!config.useMusic) return; float volume = stack.popFloat();
musicOn = true; // Set the new volume
fading = Fade.None; if(sID) alSourcef(sID, AL_GAIN, volume);
playNext(); }
void playSound()
{
if(!sID || !config.useMusic)
return;
alSourcePlay(sID);
checkALError("starting music");
}
void stopSound()
{
// How to stop / pause music
if(sID) alSourcePause(sID);
}
bool isPlaying()
{
ALint state;
alGetSourcei(sID, AL_SOURCE_STATE, &state);
return state == AL_PLAYING;
}
void updateBuffers()
{
if(!sID || !isPlaying)
return;
// Get the number of processed buffers
ALint count;
alGetSourcei(sID, AL_BUFFERS_PROCESSED, &count);
checkALError();
for(int i = 0;i < count;i++)
{
int length = avc_getAVAudioData(audioHandle, outData.ptr, outData.length);
if(length <= 0)
break;
ALuint bid;
alSourceUnqueueBuffers(sID, 1, &bid);
if(noALError())
{
alBufferData(bid, bufFormat, outData.ptr, length, bufRate);
alSourceQueueBuffers(sID, 1, &bid);
checkALError();
}
}
} }
// Disable music // Disable music
void disableMusic() void shutdown()
{ {
mo.call("stop");
if(fileHandle) avc_closeAVFile(fileHandle); if(fileHandle) avc_closeAVFile(fileHandle);
fileHandle = null; fileHandle = null;
audioHandle = null; audioHandle = null;
@ -297,109 +333,7 @@ struct MusicManager
} }
alDeleteBuffers(bIDs.length, bIDs.ptr); alDeleteBuffers(bIDs.length, bIDs.ptr);
checkALError("deleting music buffers");
bIDs[] = 0;
musicOn = false;
}
// Pause current track
void pauseMusic()
{
fading = Fade.Out;
}
// Resume. Can also be called in place of enableMusic for fading in.
void resumeMusic()
{
if(!config.useMusic) return;
volume = 0.0;
fading = Fade.In;
musicOn = true;
if(sID) addTime(0);
else playNext();
}
// Checks if a stream is playing, filling more data as needed, and restarting
// if it stalled or was paused.
private bool isPlaying()
{
if(!sID) return false;
ALint count;
alGetSourcei(sID, AL_BUFFERS_PROCESSED, &count);
if(checkALError("in isPlaying()")) return false;
for(int i = 0;i < count;i++)
{
int length = avc_getAVAudioData(audioHandle, outData.ptr, outData.length);
if(length <= 0)
{
if(i == 0)
{
ALint state;
alGetSourcei(sID, AL_SOURCE_STATE, &state);
if(checkALError() || state == AL_STOPPED)
return false;
}
break;
}
ALuint bid;
alSourceUnqueueBuffers(sID, 1, &bid);
if(checkALError() == AL_NO_ERROR)
{
alBufferData(bid, bufFormat, outData.ptr, length, bufRate);
alSourceQueueBuffers(sID, 1, &bid);
checkALError(); checkALError();
} bIDs[] = 0;
}
ALint state = AL_PLAYING;
alGetSourcei(sID, AL_SOURCE_STATE, &state);
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
if(fading == Fade.In)
{
volume += fadeInRate * time;
if(volume >= maxVolume)
{
fading = Fade.None;
volume = maxVolume;
}
}
else
{
assert(fading == Fade.Out);
volume -= fadeOutRate * time;
if(volume <= 0.0)
{
fading = Fade.None;
volume = 0.0;
// 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
if(sID) alSourcef(sID, AL_GAIN, volume);
}
} }
} }

View File

@ -119,7 +119,7 @@ struct SoundFile
{ {
alGenBuffers(1, &bID); alGenBuffers(1, &bID);
alBufferData(bID, fmt, outData.ptr, total, rate); alBufferData(bID, fmt, outData.ptr, total, rate);
if(checkALError()) if(!noALError())
{ {
writefln("Unable to load sound %s", file); writefln("Unable to load sound %s", file);
alDeleteBuffers(1, &bID); alDeleteBuffers(1, &bID);
@ -140,11 +140,11 @@ struct SoundFile
SoundInstance si; SoundInstance si;
si.owner = this; si.owner = this;
alGenSources(1, &si.inst); alGenSources(1, &si.inst);
if(checkALError() || !si.inst) if(!noALError() || !si.inst)
fail("Failed to instantiate sound resource"); fail("Failed to instantiate sound resource");
alSourcei(si.inst, AL_BUFFER, cast(ALint)bID); alSourcei(si.inst, AL_BUFFER, cast(ALint)bID);
if(checkALError()) if(!noALError())
{ {
alDeleteSources(1, &si.inst); alDeleteSources(1, &si.inst);
fail("Failed to load sound resource"); fail("Failed to load sound resource");
@ -237,7 +237,7 @@ struct SoundInstance
alGetSourcef(inst, AL_MAX_DISTANCE, &dist); alGetSourcef(inst, AL_MAX_DISTANCE, &dist);
alGetSourcefv(inst, AL_POSITION, p.ptr); alGetSourcefv(inst, AL_POSITION, p.ptr);
alGetListenerfv(AL_POSITION, lp.ptr); alGetListenerfv(AL_POSITION, lp.ptr);
if(!checkALError("updating sound position")) if(noALError())
{ {
p[0] -= lp[0]; p[0] -= lp[0];
p[1] -= lp[1]; p[1] -= lp[1];