1 /* 2 * zlib License 3 * 4 * (C) 2016 jython234 5 * 6 * This software is provided 'as-is', without any express or implied 7 * warranty. In no event will the authors be held liable for any damages 8 * arising from the use of this software. 9 * 10 * Permission is granted to anyone to use this software for any purpose, 11 * including commercial applications, and to alter it and redistribute it 12 * freely, subject to the following restrictions: 13 * 14 * 1. The origin of this software must not be misrepresented; you must not 15 * claim that you wrote the original software. If you use this software 16 * in a product, an acknowledgment in the product documentation would be 17 * appreciated but is not required. 18 * 2. Altered source versions must be plainly marked as such, and must not be 19 * misrepresented as being the original software. 20 * 3. This notice may not be removed or altered from any source distribution. 21 */ 22 module blocksound.backend.openal; 23 24 version(blocksound_ALBackend) { 25 26 pragma(msg, "-----Using OpenAL backend-----"); 27 28 import blocksound.core; 29 import blocksound.backend.types; 30 31 import derelict.openal.al; 32 import derelict.sndfile.sndfile; 33 34 /// Class to manage the OpenAL Audio backend. 35 class ALAudioBackend : AudioBackend { 36 protected ALCdevice* device; 37 protected ALCcontext* context; 38 39 /// Create a new ALBackend. One per thread. 40 this() @trusted { 41 debug(blocksound_verbose) { 42 import std.stdio : writeln; 43 writeln("[BlockSound]: Initializing OpenAL backend..."); 44 } 45 46 device = alcOpenDevice(null); // Open default device. 47 context = alcCreateContext(device, null); 48 49 alcMakeContextCurrent(context); 50 51 debug(blocksound_verbose) { 52 import std.stdio : writeln; 53 writeln("[BlockSound]: OpenAL Backend initialized."); 54 writeln("[BlockSound]: AL_VERSION: ", toDString(alGetString(AL_VERSION)), ", AL_VENDOR: ", toDString(alGetString(AL_VENDOR))); 55 } 56 } 57 58 override { 59 void setListenerLocation(in Vec3 loc) @trusted nothrow { 60 alListener3f(AL_POSITION, loc.x, loc.y, loc.z); 61 } 62 63 void setListenerGain(in float gain) @trusted nothrow { 64 alListenerf(AL_GAIN, gain); 65 } 66 67 void cleanup() @trusted nothrow { 68 alcCloseDevice(device); 69 } 70 } 71 } 72 73 /// OpenAL Source backend 74 class ALSource : Source { 75 private ALuint source; 76 77 package this() { 78 alGenSources(1, &source); 79 } 80 81 override { 82 protected void _setSound(Sound sound) @trusted { 83 if(auto s = cast(ALSound) sound) { 84 alSourcei(source, AL_BUFFER, s.buffer); 85 } else { 86 throw new Exception("Invalid Sound: not instance of ALSound"); 87 } 88 } 89 90 void setLooping(in bool loop) @trusted { 91 alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); 92 } 93 94 void play() @trusted nothrow { 95 alSourcePlay(source); 96 } 97 98 void pause() @trusted nothrow { 99 alSourcePause(source); 100 } 101 102 void stop() @trusted nothrow { 103 alSourceStop(source); 104 } 105 106 bool hasFinishedPlaying() @trusted nothrow { 107 ALenum state; 108 alGetSourcei(source, AL_SOURCE_STATE, &state); 109 return state != AL_PLAYING; 110 } 111 112 protected void _cleanup() @system nothrow { 113 alDeleteSources(1, &source); 114 } 115 } 116 } 117 118 /// OpenAL Sound backend 119 class ALSound : Sound { 120 private ALuint _buffer; 121 122 @property ALuint buffer() @safe nothrow { return _buffer; } 123 124 protected this(ALuint buffer) @safe nothrow { 125 _buffer = buffer; 126 } 127 128 static ALSound loadSound(in string filename) @trusted { 129 return new ALSound(loadSoundToBuffer(filename)); 130 } 131 132 override void cleanup() @trusted nothrow { 133 alDeleteBuffers(1, &_buffer); 134 } 135 } 136 137 /++ 138 Loads a sound from a file into an OpenAL buffer. 139 Uses libsndfile for file reading. 140 141 Params: 142 filename = The filename where the sound is located. 143 144 Throws: Exception if file is not found, or engine is not initialized. 145 Returns: An OpenAL buffer containing the sound. 146 +/ 147 ALuint loadSoundToBuffer(in string filename) @system { 148 import std.exception : enforce; 149 import std.file : exists; 150 151 enforce(INIT, new Exception("BlockSound has not been initialized!")); 152 enforce(exists(filename), new Exception("File \"" ~ filename ~ "\" does not exist!")); 153 154 SF_INFO info; 155 SNDFILE* file = sf_open(toCString(filename), SFM_READ, &info); 156 157 // Fix for OGG pops and crackles. 158 sf_command(file, SFC_SET_SCALE_FLOAT_INT_READ, cast(void*) 1, cast(int) byte.sizeof); 159 160 short[] data; 161 short[] readBuf = new short[4096]; 162 163 long readSize = 0; 164 while((readSize = sf_read_short(file, readBuf.ptr, readBuf.length)) != 0) { 165 data ~= readBuf[0..(cast(size_t) readSize)]; 166 } 167 168 ALuint buffer; 169 alGenBuffers(1, &buffer); 170 alBufferData(buffer, info.channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data.ptr, cast(int) (data.length * short.sizeof), info.samplerate); 171 172 sf_close(file); 173 174 return buffer; 175 } 176 177 /++ 178 Loads libraries required by the OpenAL backend. 179 This is called automatically by blocksound's init 180 function. 181 182 Params: 183 skipALload = Skips loading OpenAL from derelict. 184 Set this to true if your application loads 185 OpenAL itself before blocksound does. 186 187 skipSFLoad = Skips loading libsndfile from derelict. 188 Set this to true if your application loads 189 libsdnfile itself before blocksound does. 190 +/ 191 void loadLibraries(bool skipALload = false, bool skipSFload = false) @system { 192 if(!skipALload) { 193 version(Windows) { 194 try { 195 DerelictAL.load(); // Search for system libraries first. 196 debug(blocksound_verbose) notifyLoadLib("OpenAL"); 197 } catch(Exception e) { 198 DerelictAL.load("lib\\openal32.dll"); // Try to use provided library. 199 debug(blocksound_verbose) notifyLoadLib("OpenAL"); 200 } 201 } else { 202 DerelictAL.load(); 203 debug(blocksound_verbose) notifyLoadLib("OpenAL"); 204 } 205 } 206 207 if(!skipSFload) { 208 version(Windows) { 209 try { 210 DerelictSndFile.load(); // Search for system libraries first. 211 debug(blocksound_verbose) notifyLoadLib("libsndfile"); 212 } catch(Exception e) { 213 DerelictSndFile.load("lib\\libsndfile-1.dll"); // Try to use provided library. 214 debug(blocksound_verbose) notifyLoadLib("libsndfile"); 215 } 216 } else { 217 DerelictSndFile.load(); 218 debug(blocksound_verbose) notifyLoadLib("libsndfile"); 219 } 220 } 221 } 222 }