1 module polyplex.core.audio.soundeffect; 2 import polyplex.core.audio; 3 import polyplex.core.audio.effect; 4 import ppc.types.audio; 5 import openal; 6 import ppc.backend.cfile; 7 import polyplex.math; 8 import polyplex.utils.logging; 9 10 /// A sound effect which is persistent in memory. 11 public class SoundEffect { 12 private: 13 // Buffer 14 Audio stream; 15 byte[] streamBuffer; 16 ALuint buffer; 17 AudioRenderFormats fFormat; 18 19 // Source 20 ALuint source; 21 22 // Effects & filters 23 AudioEffect attachedEffect; 24 AudioFilter attachedFilter; 25 26 void applyEffectsAndFilters() { 27 alGetError(); 28 ALuint efId = attachedEffect !is null ? attachedEffect.Id : AL_EFFECTSLOT_NULL; 29 ALuint flId = attachedFilter !is null ? attachedFilter.Id : AL_FILTER_NULL; 30 31 Logger.Debug("Applying effect {0} and filter {1} on SoundEffect {2}...", efId, flId, source); 32 33 alSource3i(source, AL_AUXILIARY_SEND_FILTER, efId, 0, flId); 34 alSourcei(source, AL_DIRECT_FILTER, flId); 35 36 import std.conv; 37 ErrCodes err = cast(ErrCodes)alGetError(); 38 if (cast(ALint)err != AL_NO_ERROR) throw new Exception("Failed to create object "~err.to!string); 39 } 40 41 public: 42 43 /// Creates a new sound effect from an audio stream 44 this(Audio audio, AudioRenderFormats format = AudioRenderFormats.Auto) { 45 stream = audio; 46 import std.stdio; 47 48 // Select format if told to. 49 if (format == AudioRenderFormats.Auto) { 50 import std.conv; 51 if (stream.info.channels == 1) fFormat = AudioRenderFormats.Mono16; 52 else if (stream.info.channels == 2) fFormat = AudioRenderFormats.Stereo16; 53 else throw new Exception("Unsupported amount of channels! " ~ stream.info.channels.to!string); 54 } 55 56 // Clear errors 57 alGetError(); 58 59 // Buffer data from audio source. 60 alGenBuffers(1, &buffer); 61 streamBuffer = audio.readAll; 62 alBufferData(buffer, fFormat, streamBuffer.ptr, cast(int)streamBuffer.length, cast(int)stream.info.bitrate); 63 64 // Create audio source 65 alGenSources(1, &source); 66 alSourcei(source, AL_BUFFER, buffer); 67 } 68 69 ~this() { 70 alDeleteSources(1, &source); 71 alDeleteBuffers(1, &buffer); 72 } 73 74 /// Creates a new sound effect from a file. 75 this(string file) { 76 this(Audio(loadFile(file)), AudioRenderFormats.Auto); 77 } 78 79 void Play(bool looping = false) { 80 this.Looping = looping; 81 alSourcePlay(source); 82 } 83 84 void Pause() { 85 alSourcePause(source); 86 } 87 88 void Stop() { 89 alSourceStop(source); 90 } 91 92 void Rewind() { 93 alSourceRewind(source); 94 } 95 96 bool IsPlaying() { 97 ALenum state; 98 alGetSourcei(source, AL_SOURCE_STATE, &state); 99 return (state == AL_PLAYING); 100 } 101 102 @property AudioEffect Effect() { 103 return this.attachedEffect; 104 } 105 106 @property void Effect(AudioEffect effect) { 107 attachedEffect = effect; 108 applyEffectsAndFilters(); 109 } 110 111 @property AudioFilter Filter() { 112 return this.attachedFilter; 113 } 114 115 @property void Filter(AudioFilter filter) { 116 attachedFilter = filter; 117 applyEffectsAndFilters(); 118 } 119 120 @property bool Looping() { 121 int v = 0; 122 alGetSourcei(source, AL_LOOPING, &v); 123 return (v == 1); 124 } 125 @property void Looping(bool val) { alSourcei(source, AL_LOOPING, cast(int)val); } 126 127 @property int ByteOffset() { 128 int v = 0; 129 alGetSourcei(source, AL_BYTE_OFFSET, &v); 130 return v; 131 } 132 133 @property int SecondOffset() { 134 int v = 0; 135 alGetSourcei(source, AL_SEC_OFFSET, &v); 136 return v; 137 } 138 139 @property int SampleOffset() { 140 int v = 0; 141 alGetSourcei(source, AL_SAMPLE_OFFSET, &v); 142 return v; 143 } 144 145 /* 146 PITCH 147 */ 148 @property float Pitch() { 149 float v = 0f; 150 alGetSourcef(source, AL_PITCH, &v); 151 return v; 152 } 153 @property void Pitch(float val) { alSourcef(source, AL_PITCH, val); } 154 155 /* 156 GAIN 157 */ 158 @property float Gain() { 159 float v = 0f; 160 alGetSourcef(source, AL_GAIN, &v); 161 return v; 162 } 163 @property void Gain(float val) { alSourcef(source, AL_GAIN, val); } 164 165 /* 166 MIN GAIN 167 */ 168 @property float MinGain() { 169 float v = 0f; 170 alGetSourcef(source, AL_MIN_GAIN, &v); 171 return v; 172 } 173 @property void MinGain(float val) { alSourcef(source, AL_MIN_GAIN, val); } 174 175 /* 176 MAX GAIN 177 */ 178 @property float MaxGain() { 179 float v = 0f; 180 alGetSourcef(source, AL_MAX_GAIN, &v); 181 return v; 182 } 183 @property void MaxGain(float val) { alSourcef(source, AL_MAX_GAIN, val); } 184 185 /* 186 MAX DISTANCE 187 */ 188 @property float MaxDistance() { 189 float v = 0f; 190 alGetSourcef(source, AL_MAX_DISTANCE, &v); 191 return v; 192 } 193 @property void MaxDistance(float val) { alSourcef(source, AL_MAX_DISTANCE, val); } 194 195 /* 196 ROLLOFF FACTOR 197 */ 198 @property float RolloffFactor() { 199 float v = 0f; 200 alGetSourcef(source, AL_ROLLOFF_FACTOR, &v); 201 return v; 202 } 203 @property void RolloffFactor(float val) { alSourcef(source, AL_ROLLOFF_FACTOR, val); } 204 205 /* 206 CONE OUTER GAIN 207 */ 208 @property float ConeOuterGain() { 209 float v = 0f; 210 alGetSourcef(source, AL_CONE_OUTER_GAIN, &v); 211 return v; 212 } 213 @property void ConeOuterGain(float val) { alSourcef(source, AL_CONE_OUTER_GAIN, val); } 214 215 /* 216 CONE INNER ANGLE 217 */ 218 @property float ConeInnerAngle() { 219 float v = 0f; 220 alGetSourcef(source, AL_CONE_INNER_ANGLE, &v); 221 return v; 222 } 223 @property void ConeInnerAngle(float val) { alSourcef(source, AL_CONE_INNER_ANGLE, val); } 224 225 /* 226 CONE OUTER ANGLE 227 */ 228 @property float ConeOuterAngle() { 229 float v = 0f; 230 alGetSourcef(source, AL_CONE_OUTER_ANGLE, &v); 231 return v; 232 } 233 @property void ConeOuterAngle(float val) { alSourcef(source, AL_CONE_OUTER_ANGLE, val); } 234 235 /* 236 REFERENCE DISTANCE 237 */ 238 @property float ReferenceDistance() { 239 float v = 0f; 240 alGetSourcef(source, AL_REFERENCE_DISTANCE, &v); 241 return v; 242 } 243 @property void ReferenceDistance(float val) { alSourcef(source, AL_REFERENCE_DISTANCE, val); } 244 245 /* 246 POSITION 247 */ 248 @property Vector3 Position() { 249 Vector3 v = 0f; 250 alGetSourcefv(source, AL_POSITION, v.ptr); 251 return v; 252 } 253 @property void Position(Vector3 val) { alSourcefv(source, AL_POSITION, val.ptr); } 254 255 /* 256 VELOCITY 257 */ 258 @property Vector3 Velocity() { 259 Vector3 v = 0f; 260 alGetSourcefv(source, AL_VELOCITY, v.ptr); 261 return v; 262 } 263 @property void Velocity(Vector3 val) { alSourcefv(source, AL_VELOCITY, val.ptr); } 264 265 /* 266 DIRECTION 267 */ 268 @property Vector3 Direction() { 269 Vector3 v = 0f; 270 alGetSourcefv(source, AL_DIRECTION, v.ptr); 271 return v; 272 } 273 @property void Direction(Vector3 val) { alSourcefv(source, AL_DIRECTION, val.ptr); } 274 275 }