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 }