1 module prova.audio.audio; 2 3 import derelict.openal.al, 4 prova.audio, 5 prova.core, 6 prova.math; 7 8 private class AudioBuffer 9 { 10 uint id; 11 int channels; 12 13 ~this() 14 { 15 alDeleteBuffers(1, &id); 16 } 17 } 18 19 /// 20 class Audio 21 { 22 /// What distance in units equals one meter (defaults to 1) 23 static float scale = 1; 24 package(prova) static ALCdevice* device; 25 package(prova) static ALCcontext* context; 26 private static AudioBuffer[string] bufferCache; 27 28 package(prova) Entity entity; 29 private uint sourceId; 30 private AudioBuffer buffer; 31 private bool _looping = false; 32 private float _volume = 1; 33 private float _pitch = 1; 34 35 /// Limited to Ogg files for now 36 this(string path) 37 { 38 if(!(path in bufferCache)) 39 cacheFile(path); 40 41 buffer = bufferCache[path]; 42 43 alGenSources(1, &sourceId); 44 alSourcei(sourceId, AL_BUFFER, buffer.id); 45 } 46 47 /// 48 @property uint channels() 49 { 50 return buffer.channels; 51 } 52 53 /// 54 @property float volume() 55 { 56 return _volume; 57 } 58 59 /// 60 @property void volume(float value) 61 { 62 _volume = value; 63 alSourcef(sourceId, AL_GAIN, value); 64 } 65 66 /// 67 @property float pitch() 68 { 69 return _pitch; 70 } 71 72 /// 73 @property void pitch(float value) 74 { 75 _pitch = value; 76 alSourcef(sourceId, AL_PITCH, value); 77 } 78 79 /// 80 @property bool isPlaying() 81 { 82 ALenum status; 83 alGetSourcei(sourceId, AL_SOURCE_STATE, &status); 84 85 return status == AL_PLAYING; 86 } 87 88 /// 89 @property bool looping() 90 { 91 return _looping; 92 } 93 94 /// 95 void play(bool loop = false) 96 { 97 if(isPlaying()) 98 stop(); 99 100 _looping = loop; 101 alSourcei(sourceId, AL_LOOPING, loop); 102 103 alSourceRewind(sourceId); 104 alSourcePlay(sourceId); 105 } 106 107 /// 108 void stop() 109 { 110 alSourceStop(sourceId); 111 } 112 113 /// 114 void pause() 115 { 116 alSourcePause(sourceId); 117 } 118 119 /// 120 void resume() 121 { 122 alSourcePlay(sourceId); 123 } 124 125 package(prova) void update() 126 { 127 if(!entity) 128 return; 129 130 Vector3 position = entity.position / scale; 131 Vector3 velocity = entity.velocity / scale; 132 133 alSource3f(sourceId, AL_POSITION, position.x, position.y, position.z); 134 alSource3f(sourceId, AL_VELOCITY, velocity.x, velocity.y, velocity.z); 135 } 136 137 ~this() 138 { 139 if(isPlaying) 140 stop(); 141 142 alDeleteSources(1, &sourceId); 143 } 144 145 /// Limited to Ogg files for now 146 static void cacheFile(string path) 147 { 148 AudioFile file = new OggFile(path); 149 150 genBuffer(path, file.channels, file.data, file.frequency); 151 } 152 153 private static void genBuffer(string path, int channels, byte[] data, int frequency) 154 { 155 AudioBuffer buffer = new AudioBuffer(); 156 buffer.channels = channels; 157 158 ALenum format = channels ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; 159 160 alGenBuffers(1, &buffer.id); 161 alBufferData(buffer.id, format, data.ptr, cast(int) data.length, frequency); 162 163 bufferCache[path] = buffer; 164 } 165 166 package(prova) static void cleanUp() 167 { 168 bufferCache.clear(); 169 } 170 }