소스 검색

Add extension libraries

Pabloader 4 년 전
부모
커밋
d3b17a7ee2
3개의 변경된 파일1236개의 추가작업 그리고 0개의 파일을 삭제
  1. 31
    0
      LICENCE.md
  2. 313
    0
      src/olcPGEX_Graphics2D.h
  3. 892
    0
      src/olcPGEX_Sound.h

+ 31
- 0
LICENCE.md 파일 보기

@@ -0,0 +1,31 @@
1
+# License (OLC-3)
2
+
3
+Copyright 2018-2019 OneLoneCoder.com, Pabloader
4
+
5
+Redistribution and use in source and binary forms, with or without 
6
+modification, are permitted provided that the following conditions 
7
+are met:
8
+
9
+1. Redistributions or derivations of source code must retain the above 
10
+   copyright notice, this list of conditions and the following disclaimer.
11
+
12
+2. Redistributions or derivative works in binary form must reproduce 
13
+   the above copyright notice. This list of conditions and the following 
14
+   disclaimer must be reproduced in the documentation and/or other 
15
+   materials provided with the distribution.
16
+
17
+3. Neither the name of the copyright holder nor the names of its 
18
+   contributors may be used to endorse or promote products derived 
19
+   from this software without specific prior written permission.
20
+    
21
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
24
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
25
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
27
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
28
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
29
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
30
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 313
- 0
src/olcPGEX_Graphics2D.h 파일 보기

@@ -0,0 +1,313 @@
1
+/*
2
+	olcPGEX_Graphics2D.h
3
+
4
+	+-------------------------------------------------------------+
5
+	|         OneLoneCoder Pixel Game Engine Extension            |
6
+	|                Advanced 2D Rendering - v0.4                 |
7
+	+-------------------------------------------------------------+
8
+
9
+	What is this?
10
+	~~~~~~~~~~~~~
11
+	This is an extension to the olcPixelGameEngine, which provides
12
+	advanced olc::Sprite manipulation and drawing routines. To use
13
+	it, simply include this header file.
14
+
15
+	License (OLC-3)
16
+	~~~~~~~~~~~~~~~
17
+
18
+	Copyright 2018 - 2019 OneLoneCoder.com
19
+
20
+	Redistribution and use in source and binary forms, with or without
21
+	modification, are permitted provided that the following conditions
22
+	are met:
23
+
24
+	1. Redistributions or derivations of source code must retain the above
25
+	copyright notice, this list of conditions and the following disclaimer.
26
+
27
+	2. Redistributions or derivative works in binary form must reproduce
28
+	the above copyright notice. This list of conditions and the following
29
+	disclaimer must be reproduced in the documentation and/or other
30
+	materials provided with the distribution.
31
+
32
+	3. Neither the name of the copyright holder nor the names of its
33
+	contributors may be used to endorse or promote products derived
34
+	from this software without specific prior written permission.
35
+
36
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
37
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
38
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
39
+	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
40
+	HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41
+	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42
+	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
43
+	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44
+	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
45
+	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
46
+	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47
+
48
+	Links
49
+	~~~~~
50
+	YouTube:	https://www.youtube.com/javidx9
51
+	Discord:	https://discord.gg/WhwHUMV
52
+	Twitter:	https://www.twitter.com/javidx9
53
+	Twitch:		https://www.twitch.tv/javidx9
54
+	GitHub:		https://www.github.com/onelonecoder
55
+	Homepage:	https://www.onelonecoder.com
56
+
57
+	Author
58
+	~~~~~~
59
+	David Barr, aka javidx9, ©OneLoneCoder 2019
60
+*/
61
+
62
+/*
63
+	Matrices stored as [Column][Row] (i.e. x, y)
64
+
65
+	|C0R0 C1R0 C2R0|   | x |   | x'|
66
+	|C0R1 C1R1 C2R1| * | y | = | y'|
67
+	|C0R2 C1R2 C2R2|   |1.0|   | - |
68
+*/
69
+
70
+
71
+
72
+#ifndef OLC_PGEX_GFX2D
73
+#define OLC_PGEX_GFX2D
74
+
75
+#include <algorithm>
76
+#undef min
77
+#undef max
78
+
79
+namespace olc
80
+{
81
+	// Container class for Advanced 2D Drawing functions
82
+	class GFX2D : public olc::PGEX
83
+	{
84
+		// A representation of an affine transform, used to rotate, scale, offset & shear space
85
+	public:
86
+		class Transform2D
87
+		{
88
+		public:
89
+			inline Transform2D();
90
+
91
+		public:
92
+			// Set this transformation to unity
93
+			inline void Reset();
94
+			// Append a rotation of fTheta radians to this transform
95
+			inline void Rotate(float fTheta);
96
+			// Append a translation (ox, oy) to this transform
97
+			inline void Translate(float ox, float oy);
98
+			// Append a scaling operation (sx, sy) to this transform
99
+			inline void Scale(float sx, float sy);
100
+			// Append a shear operation (sx, sy) to this transform
101
+			inline void Shear(float sx, float sy);
102
+
103
+			inline void Perspective(float ox, float oy);
104
+			// Calculate the Forward Transformation of the coordinate (in_x, in_y) -> (out_x, out_y)
105
+			inline void Forward(float in_x, float in_y, float &out_x, float &out_y);
106
+			// Calculate the Inverse Transformation of the coordinate (in_x, in_y) -> (out_x, out_y)
107
+			inline void Backward(float in_x, float in_y, float &out_x, float &out_y);
108
+			// Regenerate the Inverse Transformation
109
+			inline void Invert();
110
+
111
+		private:
112
+			inline void Multiply();
113
+			float matrix[4][3][3];
114
+			int nTargetMatrix;
115
+			int nSourceMatrix;
116
+			bool bDirty;
117
+		};
118
+
119
+	public:
120
+		// Draws a sprite with the transform applied
121
+		inline static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform);
122
+	};
123
+}
124
+
125
+
126
+#ifdef OLC_PGE_GRAPHICS2D
127
+#undef OLC_PGE_GRAPHICS2D
128
+
129
+namespace olc
130
+{
131
+	void GFX2D::DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform)
132
+	{
133
+		if (sprite == nullptr)
134
+			return;
135
+
136
+		// Work out bounding rectangle of sprite
137
+		float ex, ey;
138
+		float sx, sy;		
139
+		float px, py;
140
+
141
+		transform.Forward(0.0f, 0.0f, sx, sy);
142
+		px = sx; py = sy;
143
+		sx = std::min(sx, px); sy = std::min(sy, py);
144
+		ex = std::max(ex, px); ey = std::max(ey, py);
145
+		
146
+		transform.Forward((float)sprite->width, (float)sprite->height, px, py);
147
+		sx = std::min(sx, px); sy = std::min(sy, py);
148
+		ex = std::max(ex, px); ey = std::max(ey, py);
149
+
150
+		transform.Forward(0.0f, (float)sprite->height, px, py);
151
+		sx = std::min(sx, px); sy = std::min(sy, py);
152
+		ex = std::max(ex, px); ey = std::max(ey, py);
153
+
154
+		transform.Forward((float)sprite->width, 0.0f, px, py);
155
+		sx = std::min(sx, px); sy = std::min(sy, py);
156
+		ex = std::max(ex, px); ey = std::max(ey, py);
157
+
158
+		// Perform inversion of transform if required
159
+		transform.Invert();
160
+
161
+		if (ex < sx) 
162
+			std::swap(ex, sx);
163
+		if (ey < sy) 
164
+			std::swap(ey, sy);
165
+
166
+		// Iterate through render space, and sample Sprite from suitable texel location
167
+		for (float i = sx; i < ex; i++)
168
+		{
169
+			for (float j = sy; j < ey; j++)
170
+			{
171
+				float ox, oy;
172
+				transform.Backward(i, j, ox, oy);
173
+				pge->Draw((int32_t)i, (int32_t)j, sprite->GetPixel((int32_t)(ox+0.5f), (int32_t)(oy+0.5f)));
174
+			}
175
+		}
176
+	}
177
+
178
+	olc::GFX2D::Transform2D::Transform2D()
179
+	{
180
+		Reset();
181
+	}
182
+
183
+	void olc::GFX2D::Transform2D::Reset()
184
+	{
185
+		nTargetMatrix = 0;
186
+		nSourceMatrix = 1;
187
+		bDirty = true;
188
+
189
+		// Columns Then Rows
190
+
191
+		// Matrices 0 & 1 are used as swaps in Transform accumulation
192
+		matrix[0][0][0] = 1.0f; matrix[0][1][0] = 0.0f; matrix[0][2][0] = 0.0f;
193
+		matrix[0][0][1] = 0.0f; matrix[0][1][1] = 1.0f; matrix[0][2][1] = 0.0f;
194
+		matrix[0][0][2] = 0.0f; matrix[0][1][2] = 0.0f; matrix[0][2][2] = 1.0f;
195
+
196
+		matrix[1][0][0] = 1.0f; matrix[1][1][0] = 0.0f; matrix[1][2][0] = 0.0f;
197
+		matrix[1][0][1] = 0.0f; matrix[1][1][1] = 1.0f; matrix[1][2][1] = 0.0f;
198
+		matrix[1][0][2] = 0.0f; matrix[1][1][2] = 0.0f; matrix[1][2][2] = 1.0f;
199
+
200
+		// Matrix 2 is a cache matrix to hold the immediate transform operation
201
+		// Matrix 3 is a cache matrix to hold the inverted transform
202
+	}
203
+
204
+	void olc::GFX2D::Transform2D::Multiply()
205
+	{
206
+		for (int c = 0; c < 3; c++)
207
+		{
208
+			for (int r = 0; r < 3; r++)
209
+			{
210
+				matrix[nTargetMatrix][c][r] = matrix[2][0][r] * matrix[nSourceMatrix][c][0] +
211
+											  matrix[2][1][r] * matrix[nSourceMatrix][c][1] +
212
+											  matrix[2][2][r] * matrix[nSourceMatrix][c][2];
213
+			}
214
+		}
215
+
216
+		std::swap(nTargetMatrix, nSourceMatrix);
217
+		bDirty = true; // Any transform multiply dirties the inversion
218
+	}
219
+
220
+	void olc::GFX2D::Transform2D::Rotate(float fTheta)
221
+	{		
222
+		// Construct Rotation Matrix
223
+		matrix[2][0][0] = cosf(fTheta);  matrix[2][1][0] = sinf(fTheta); matrix[2][2][0] = 0.0f;
224
+		matrix[2][0][1] = -sinf(fTheta); matrix[2][1][1] = cosf(fTheta); matrix[2][2][1] = 0.0f;
225
+		matrix[2][0][2] = 0.0f;			 matrix[2][1][2] = 0.0f;		 matrix[2][2][2] = 1.0f;
226
+		Multiply();		
227
+	}
228
+
229
+	void olc::GFX2D::Transform2D::Scale(float sx, float sy)
230
+	{
231
+		// Construct Scale Matrix
232
+		matrix[2][0][0] = sx;   matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f;
233
+		matrix[2][0][1] = 0.0f; matrix[2][1][1] = sy;   matrix[2][2][1] = 0.0f;
234
+		matrix[2][0][2] = 0.0f;	matrix[2][1][2] = 0.0f;	matrix[2][2][2] = 1.0f;
235
+		Multiply();
236
+	}
237
+
238
+	void olc::GFX2D::Transform2D::Shear(float sx, float sy)
239
+	{
240
+		// Construct Shear Matrix		
241
+		matrix[2][0][0] = 1.0f; matrix[2][1][0] = sx;   matrix[2][2][0] = 0.0f;
242
+		matrix[2][0][1] = sy;   matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f;
243
+		matrix[2][0][2] = 0.0f;	matrix[2][1][2] = 0.0f;	matrix[2][2][2] = 1.0f;
244
+		Multiply();
245
+	}
246
+
247
+	void olc::GFX2D::Transform2D::Translate(float ox, float oy)
248
+	{
249
+		// Construct Translate Matrix
250
+		matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = ox;
251
+		matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = oy;
252
+		matrix[2][0][2] = 0.0f;	matrix[2][1][2] = 0.0f;	matrix[2][2][2] = 1.0f;
253
+		Multiply();
254
+	}
255
+
256
+	void olc::GFX2D::Transform2D::Perspective(float ox, float oy)
257
+	{
258
+		// Construct Translate Matrix
259
+		matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f;
260
+		matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f;
261
+		matrix[2][0][2] = ox;	matrix[2][1][2] = oy;	matrix[2][2][2] = 1.0f;
262
+		Multiply();
263
+	}
264
+
265
+	void olc::GFX2D::Transform2D::Forward(float in_x, float in_y, float &out_x, float &out_y)
266
+	{
267
+		out_x = in_x * matrix[nSourceMatrix][0][0] + in_y * matrix[nSourceMatrix][1][0] + matrix[nSourceMatrix][2][0];
268
+		out_y = in_x * matrix[nSourceMatrix][0][1] + in_y * matrix[nSourceMatrix][1][1] + matrix[nSourceMatrix][2][1];
269
+		float out_z = in_x * matrix[nSourceMatrix][0][2] + in_y * matrix[nSourceMatrix][1][2] + matrix[nSourceMatrix][2][2];
270
+		if (out_z != 0)
271
+		{
272
+			out_x /= out_z;
273
+			out_y /= out_z;
274
+		}
275
+	} 
276
+
277
+	void olc::GFX2D::Transform2D::Backward(float in_x, float in_y, float &out_x, float &out_y)
278
+	{
279
+		out_x = in_x * matrix[3][0][0] + in_y * matrix[3][1][0] + matrix[3][2][0];
280
+		out_y = in_x * matrix[3][0][1] + in_y * matrix[3][1][1] + matrix[3][2][1];
281
+		float out_z = in_x * matrix[3][0][2] + in_y * matrix[3][1][2] + matrix[3][2][2];
282
+		if (out_z != 0)
283
+		{
284
+			out_x /= out_z;
285
+			out_y /= out_z;
286
+		}
287
+	}
288
+
289
+	void olc::GFX2D::Transform2D::Invert()
290
+	{
291
+		if (bDirty) // Obviously costly so only do if needed
292
+		{			
293
+			float det = matrix[nSourceMatrix][0][0] * (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) -
294
+				        matrix[nSourceMatrix][1][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2]) +
295
+				        matrix[nSourceMatrix][2][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][0][2]);
296
+
297
+			float idet = 1.0f / det;
298
+			matrix[3][0][0] = (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) * idet;
299
+			matrix[3][1][0] = (matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][2]) * idet;
300
+			matrix[3][2][0] = (matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][1] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][1]) * idet;
301
+			matrix[3][0][1] = (matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2]) * idet;
302
+			matrix[3][1][1] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][0][2]) * idet;
303
+			matrix[3][2][1] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][1]) * idet;
304
+			matrix[3][0][2] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][1]) * idet;
305
+			matrix[3][1][2] = (matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][2]) * idet;
306
+			matrix[3][2][2] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][1] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][0]) * idet;
307
+			bDirty = false;
308
+		}				
309
+	}
310
+}
311
+
312
+#endif
313
+#endif

+ 892
- 0
src/olcPGEX_Sound.h 파일 보기

@@ -0,0 +1,892 @@
1
+/*
2
+	olcPGEX_Sound.h
3
+
4
+	+-------------------------------------------------------------+
5
+	|         OneLoneCoder Pixel Game Engine Extension            |
6
+	|                       Sound - v0.3                          |
7
+	+-------------------------------------------------------------+
8
+
9
+	What is this?
10
+	~~~~~~~~~~~~~
11
+	This is an extension to the olcPixelGameEngine, which provides
12
+	sound generation and wave playing routines.
13
+
14
+	Special Thanks:
15
+	~~~~~~~~~~~~~~~	
16
+	Slavka - For entire non-windows system back end!
17
+	Gorbit99 - Testing, Bug Fixes
18
+	Cyberdroid - Testing, Bug Fixes
19
+	Dragoneye - Testing
20
+	Puol - Testing
21
+
22
+	License (OLC-3)
23
+	~~~~~~~~~~~~~~~
24
+
25
+	Copyright 2018 - 2019 OneLoneCoder.com
26
+
27
+	Redistribution and use in source and binary forms, with or without
28
+	modification, are permitted provided that the following conditions
29
+	are met:
30
+
31
+	1. Redistributions or derivations of source code must retain the above
32
+	copyright notice, this list of conditions and the following disclaimer.
33
+
34
+	2. Redistributions or derivative works in binary form must reproduce
35
+	the above copyright notice. This list of conditions and the following
36
+	disclaimer must be reproduced in the documentation and/or other
37
+	materials provided with the distribution.
38
+
39
+	3. Neither the name of the copyright holder nor the names of its
40
+	contributors may be used to endorse or promote products derived
41
+	from this software without specific prior written permission.
42
+
43
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
44
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
45
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
46
+	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
47
+	HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
48
+	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
49
+	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
50
+	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
51
+	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
52
+	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
53
+	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54
+
55
+	Links
56
+	~~~~~
57
+	YouTube:	https://www.youtube.com/javidx9
58
+	Discord:	https://discord.gg/WhwHUMV
59
+	Twitter:	https://www.twitter.com/javidx9
60
+	Twitch:		https://www.twitch.tv/javidx9
61
+	GitHub:		https://www.github.com/onelonecoder
62
+	Homepage:	https://www.onelonecoder.com
63
+	Patreon:	https://www.patreon.com/javidx9
64
+
65
+	Author
66
+	~~~~~~
67
+	David Barr, aka javidx9, ©OneLoneCoder 2019
68
+*/
69
+
70
+
71
+#ifndef OLC_PGEX_SOUND_H
72
+#define OLC_PGEX_SOUND_H
73
+
74
+#include <istream>
75
+#include <cstring>
76
+#include <climits>
77
+
78
+#include <algorithm>
79
+#undef min
80
+#undef max
81
+
82
+// Choose a default sound backend
83
+#if !defined(USE_ALSA) && !defined(USE_OPENAL) && !defined(USE_WINDOWS)
84
+#ifdef __linux__
85
+#define USE_ALSA
86
+#endif
87
+
88
+#ifdef __EMSCRIPTEN__
89
+#define USE_OPENAL
90
+#endif
91
+
92
+#ifdef _WIN32
93
+#define USE_WINDOWS
94
+#endif
95
+
96
+#endif
97
+
98
+#ifdef USE_ALSA
99
+#define ALSA_PCM_NEW_HW_PARAMS_API
100
+#include <alsa/asoundlib.h>
101
+#endif
102
+
103
+#ifdef USE_OPENAL
104
+#include <AL/al.h>
105
+#include <AL/alc.h>
106
+#include <queue>
107
+#endif
108
+
109
+#pragma pack(push, 1)
110
+typedef struct {
111
+	uint16_t wFormatTag;
112
+	uint16_t nChannels;
113
+	uint32_t nSamplesPerSec;
114
+	uint32_t nAvgBytesPerSec;
115
+	uint16_t nBlockAlign;
116
+	uint16_t wBitsPerSample;
117
+	uint16_t cbSize;
118
+} OLC_WAVEFORMATEX;
119
+#pragma pack(pop)
120
+
121
+namespace olc
122
+{
123
+	// Container class for Advanced 2D Drawing functions
124
+	class SOUND : public olc::PGEX
125
+	{
126
+		// A representation of an affine transform, used to rotate, scale, offset & shear space
127
+	public:
128
+		class AudioSample
129
+		{
130
+		public:
131
+			AudioSample();
132
+			AudioSample(std::string sWavFile, olc::ResourcePack *pack = nullptr);
133
+			olc::rcode LoadFromFile(std::string sWavFile, olc::ResourcePack *pack = nullptr);
134
+
135
+		public:
136
+			OLC_WAVEFORMATEX wavHeader;
137
+			float *fSample = nullptr;
138
+			long nSamples = 0;
139
+			int nChannels = 0;
140
+			bool bSampleValid = false;
141
+		};
142
+
143
+		struct sCurrentlyPlayingSample
144
+		{
145
+			int nAudioSampleID = 0;
146
+			long nSamplePosition = 0;
147
+			bool bFinished = false;
148
+			bool bLoop = false;
149
+			bool bFlagForStop = false;
150
+		};
151
+
152
+		static std::list<sCurrentlyPlayingSample> listActiveSamples;
153
+
154
+	public:
155
+		static bool InitialiseAudio(unsigned int nSampleRate = 44100, unsigned int nChannels = 1, unsigned int nBlocks = 8, unsigned int nBlockSamples = 512);
156
+		static bool DestroyAudio();
157
+		static void SetUserSynthFunction(std::function<float(int, float, float)> func);
158
+		static void SetUserFilterFunction(std::function<float(int, float, float)> func);
159
+
160
+	public:
161
+		static int LoadAudioSample(std::string sWavFile, olc::ResourcePack *pack = nullptr);
162
+		static void PlaySample(int id, bool bLoop = false);
163
+		static void StopSample(int id);
164
+		static void StopAll();
165
+		static float GetMixerOutput(int nChannel, float fGlobalTime, float fTimeStep);
166
+
167
+
168
+	private:
169
+#ifdef USE_WINDOWS // Windows specific sound management
170
+		static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwParam1, DWORD dwParam2);
171
+		static unsigned int m_nSampleRate;
172
+		static unsigned int m_nChannels;
173
+		static unsigned int m_nBlockCount;
174
+		static unsigned int m_nBlockSamples;
175
+		static unsigned int m_nBlockCurrent;
176
+		static short* m_pBlockMemory;
177
+		static WAVEHDR *m_pWaveHeaders;
178
+		static HWAVEOUT m_hwDevice;
179
+		static std::atomic<unsigned int> m_nBlockFree;
180
+		static std::condition_variable m_cvBlockNotZero;
181
+		static std::mutex m_muxBlockNotZero;
182
+#endif
183
+
184
+#ifdef USE_ALSA
185
+		static snd_pcm_t *m_pPCM;
186
+		static unsigned int m_nSampleRate;
187
+		static unsigned int m_nChannels;
188
+		static unsigned int m_nBlockSamples;
189
+		static short* m_pBlockMemory;
190
+#endif
191
+
192
+#ifdef USE_OPENAL
193
+		static std::queue<ALuint> m_qAvailableBuffers;
194
+		static ALuint *m_pBuffers;
195
+		static ALuint m_nSource;
196
+		static ALCdevice *m_pDevice;
197
+		static ALCcontext *m_pContext;
198
+		static unsigned int m_nSampleRate;
199
+		static unsigned int m_nChannels;
200
+		static unsigned int m_nBlockCount;
201
+		static unsigned int m_nBlockSamples;
202
+		static short* m_pBlockMemory;
203
+#endif
204
+
205
+		static void AudioThread();
206
+		static std::thread m_AudioThread;
207
+		static std::atomic<bool> m_bAudioThreadActive;
208
+		static std::atomic<float> m_fGlobalTime;
209
+		static std::function<float(int, float, float)> funcUserSynth;
210
+		static std::function<float(int, float, float)> funcUserFilter;
211
+	};
212
+}
213
+
214
+
215
+// Implementation, platform-independent
216
+
217
+#ifdef OLC_PGEX_SOUND
218
+#undef OLC_PGEX_SOUND
219
+
220
+namespace olc
221
+{
222
+	SOUND::AudioSample::AudioSample()
223
+	{	}
224
+
225
+	SOUND::AudioSample::AudioSample(std::string sWavFile, olc::ResourcePack *pack)
226
+	{
227
+		LoadFromFile(sWavFile, pack);
228
+	}
229
+
230
+	olc::rcode SOUND::AudioSample::LoadFromFile(std::string sWavFile, olc::ResourcePack *pack)
231
+	{
232
+		auto ReadWave = [&](std::istream &is)
233
+		{
234
+			char dump[4];
235
+			is.read(dump, sizeof(char) * 4); // Read "RIFF"
236
+			if (strncmp(dump, "RIFF", 4) != 0) return olc::FAIL;
237
+			is.read(dump, sizeof(char) * 4); // Not Interested
238
+			is.read(dump, sizeof(char) * 4); // Read "WAVE"
239
+			if (strncmp(dump, "WAVE", 4) != 0) return olc::FAIL;
240
+
241
+			// Read Wave description chunk
242
+			is.read(dump, sizeof(char) * 4); // Read "fmt "
243
+			unsigned int nHeaderSize = 0;
244
+			is.read((char*)&nHeaderSize, sizeof(unsigned int)); // Not Interested
245
+			is.read((char*)&wavHeader, nHeaderSize);// sizeof(WAVEFORMATEX)); // Read Wave Format Structure chunk
246
+														// Note the -2, because the structure has 2 bytes to indicate its own size
247
+														// which are not in the wav file
248
+
249
+			// Just check if wave format is compatible with olcPGE
250
+			if (wavHeader.wBitsPerSample != 16 || wavHeader.nSamplesPerSec != 44100)
251
+				return olc::FAIL;
252
+
253
+			// Search for audio data chunk
254
+			uint32_t nChunksize = 0;
255
+			is.read(dump, sizeof(char) * 4); // Read chunk header
256
+			is.read((char*)&nChunksize, sizeof(uint32_t)); // Read chunk size
257
+			while (strncmp(dump, "data", 4) != 0)
258
+			{
259
+				// Not audio data, so just skip it
260
+				//std::fseek(f, nChunksize, SEEK_CUR);
261
+				is.seekg(nChunksize, std::istream::cur);
262
+				is.read(dump, sizeof(char) * 4);
263
+				is.read((char*)&nChunksize, sizeof(uint32_t));
264
+			}
265
+
266
+			// Finally got to data, so read it all in and convert to float samples
267
+			nSamples = nChunksize / (wavHeader.nChannels * (wavHeader.wBitsPerSample >> 3));
268
+			nChannels = wavHeader.nChannels;
269
+
270
+			// Create floating point buffer to hold audio sample
271
+			fSample = new float[nSamples * nChannels];
272
+			float *pSample = fSample;
273
+
274
+			// Read in audio data and normalise
275
+			for (long i = 0; i < nSamples; i++)
276
+			{
277
+				for (int c = 0; c < nChannels; c++)
278
+				{
279
+					short s = 0;
280
+					if (!is.eof())
281
+					{
282
+						is.read((char*)&s, sizeof(short));
283
+
284
+						*pSample = (float)s / (float)(SHRT_MAX);
285
+						pSample++;
286
+					}
287
+				}
288
+			}
289
+
290
+			// All done, flag sound as valid
291
+			bSampleValid = true;
292
+			return olc::OK;
293
+		};
294
+
295
+		if (pack != nullptr)
296
+		{
297
+			olc::ResourcePack::sEntry entry = pack->GetStreamBuffer(sWavFile);
298
+			std::istream is(&entry);
299
+			return ReadWave(is);
300
+		}
301
+		else
302
+		{
303
+			// Read from file
304
+			std::ifstream ifs(sWavFile, std::ifstream::binary);
305
+			if (ifs.is_open())
306
+			{
307
+				return ReadWave(ifs);
308
+			}
309
+			else
310
+				return olc::FAIL;
311
+		}
312
+	}
313
+
314
+	// This vector holds all loaded sound samples in memory
315
+	std::vector<olc::SOUND::AudioSample> vecAudioSamples;
316
+
317
+	// This structure represents a sound that is currently playing. It only
318
+	// holds the sound ID and where this instance of it is up to for its
319
+	// current playback
320
+
321
+	void SOUND::SetUserSynthFunction(std::function<float(int, float, float)> func)
322
+	{
323
+		funcUserSynth = func;
324
+	}
325
+
326
+	void SOUND::SetUserFilterFunction(std::function<float(int, float, float)> func)
327
+	{
328
+		funcUserFilter = func;
329
+	}
330
+
331
+	// Load a 16-bit WAVE file @ 44100Hz ONLY into memory. A sample ID
332
+	// number is returned if successful, otherwise -1
333
+	int SOUND::LoadAudioSample(std::string sWavFile, olc::ResourcePack *pack)
334
+	{
335
+
336
+		olc::SOUND::AudioSample a(sWavFile, pack);
337
+		if (a.bSampleValid)
338
+		{
339
+			vecAudioSamples.push_back(a);
340
+			return (unsigned int)vecAudioSamples.size();
341
+		}
342
+		else
343
+			return -1;
344
+	}
345
+
346
+	// Add sample 'id' to the mixers sounds to play list
347
+	void SOUND::PlaySample(int id, bool bLoop)
348
+	{
349
+		olc::SOUND::sCurrentlyPlayingSample a;
350
+		a.nAudioSampleID = id;
351
+		a.nSamplePosition = 0;
352
+		a.bFinished = false;
353
+		a.bFlagForStop = false;
354
+		a.bLoop = bLoop;
355
+		SOUND::listActiveSamples.push_back(a);
356
+	}
357
+
358
+	void SOUND::StopSample(int id)
359
+	{
360
+		// Find first occurence of sample id
361
+		auto s = std::find_if(listActiveSamples.begin(), listActiveSamples.end(), [&](const olc::SOUND::sCurrentlyPlayingSample &s) { return s.nAudioSampleID == id; });
362
+		if (s != listActiveSamples.end())
363
+			s->bFlagForStop = true;
364
+	}
365
+
366
+	void SOUND::StopAll()
367
+	{
368
+		for (auto &s : listActiveSamples)
369
+		{
370
+			s.bFlagForStop = true;
371
+		}
372
+	}
373
+
374
+	float SOUND::GetMixerOutput(int nChannel, float fGlobalTime, float fTimeStep)
375
+	{
376
+		// Accumulate sample for this channel
377
+		float fMixerSample = 0.0f;
378
+
379
+		for (auto &s : listActiveSamples)
380
+		{
381
+			if (m_bAudioThreadActive)
382
+			{
383
+				if (s.bFlagForStop)
384
+				{
385
+					s.bLoop = false;
386
+					s.bFinished = true;
387
+				}
388
+				else
389
+				{
390
+					// Calculate sample position
391
+					s.nSamplePosition += roundf((float)vecAudioSamples[s.nAudioSampleID - 1].wavHeader.nSamplesPerSec * fTimeStep);
392
+
393
+					// If sample position is valid add to the mix
394
+					if (s.nSamplePosition < vecAudioSamples[s.nAudioSampleID - 1].nSamples)
395
+						fMixerSample += vecAudioSamples[s.nAudioSampleID - 1].fSample[(s.nSamplePosition * vecAudioSamples[s.nAudioSampleID - 1].nChannels) + nChannel];
396
+					else
397
+					{
398
+						if (s.bLoop)
399
+						{
400
+							s.nSamplePosition = 0;
401
+						}
402
+						else
403
+							s.bFinished = true; // Else sound has completed
404
+					}
405
+				}
406
+			}
407
+			else
408
+				return 0.0f;
409
+		}
410
+
411
+		// If sounds have completed then remove them
412
+		listActiveSamples.remove_if([](const sCurrentlyPlayingSample &s) {return s.bFinished; });
413
+
414
+		// The users application might be generating sound, so grab that if it exists
415
+		if (funcUserSynth != nullptr)
416
+			fMixerSample += funcUserSynth(nChannel, fGlobalTime, fTimeStep);
417
+
418
+		// Return the sample via an optional user override to filter the sound
419
+		if (funcUserFilter != nullptr)
420
+			return funcUserFilter(nChannel, fGlobalTime, fMixerSample);
421
+		else
422
+			return fMixerSample;
423
+	}
424
+
425
+	std::thread SOUND::m_AudioThread;
426
+	std::atomic<bool> SOUND::m_bAudioThreadActive{ false };
427
+	std::atomic<float> SOUND::m_fGlobalTime{ 0.0f };
428
+	std::list<SOUND::sCurrentlyPlayingSample> SOUND::listActiveSamples;
429
+	std::function<float(int, float, float)> SOUND::funcUserSynth = nullptr;
430
+	std::function<float(int, float, float)> SOUND::funcUserFilter = nullptr;
431
+}
432
+
433
+// Implementation, Windows-specific
434
+#ifdef USE_WINDOWS
435
+#pragma comment(lib, "winmm.lib")
436
+
437
+namespace olc
438
+{
439
+	bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples)
440
+	{
441
+		// Initialise Sound Engine
442
+		m_bAudioThreadActive = false;
443
+		m_nSampleRate = nSampleRate;
444
+		m_nChannels = nChannels;
445
+		m_nBlockCount = nBlocks;
446
+		m_nBlockSamples = nBlockSamples;
447
+		m_nBlockFree = m_nBlockCount;
448
+		m_nBlockCurrent = 0;
449
+		m_pBlockMemory = nullptr;
450
+		m_pWaveHeaders = nullptr;
451
+
452
+		// Device is available
453
+		WAVEFORMATEX waveFormat;
454
+		waveFormat.wFormatTag = WAVE_FORMAT_PCM;
455
+		waveFormat.nSamplesPerSec = m_nSampleRate;
456
+		waveFormat.wBitsPerSample = sizeof(short) * 8;
457
+		waveFormat.nChannels = m_nChannels;
458
+		waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
459
+		waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
460
+		waveFormat.cbSize = 0;
461
+
462
+		listActiveSamples.clear();
463
+
464
+		// Open Device if valid
465
+		if (waveOutOpen(&m_hwDevice, WAVE_MAPPER, &waveFormat, (DWORD_PTR)SOUND::waveOutProc, (DWORD_PTR)0, CALLBACK_FUNCTION) != S_OK)
466
+			return DestroyAudio();
467
+
468
+		// Allocate Wave|Block Memory
469
+		m_pBlockMemory = new short[m_nBlockCount * m_nBlockSamples];
470
+		if (m_pBlockMemory == nullptr)
471
+			return DestroyAudio();
472
+		ZeroMemory(m_pBlockMemory, sizeof(short) * m_nBlockCount * m_nBlockSamples);
473
+
474
+		m_pWaveHeaders = new WAVEHDR[m_nBlockCount];
475
+		if (m_pWaveHeaders == nullptr)
476
+			return DestroyAudio();
477
+		ZeroMemory(m_pWaveHeaders, sizeof(WAVEHDR) * m_nBlockCount);
478
+
479
+		// Link headers to block memory
480
+		for (unsigned int n = 0; n < m_nBlockCount; n++)
481
+		{
482
+			m_pWaveHeaders[n].dwBufferLength = m_nBlockSamples * sizeof(short);
483
+			m_pWaveHeaders[n].lpData = (LPSTR)(m_pBlockMemory + (n * m_nBlockSamples));
484
+		}
485
+
486
+		m_bAudioThreadActive = true;
487
+		m_AudioThread = std::thread(&SOUND::AudioThread);
488
+
489
+		// Start the ball rolling with the sound delivery thread
490
+		std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
491
+		m_cvBlockNotZero.notify_one();
492
+		return true;
493
+	}
494
+
495
+	// Stop and clean up audio system
496
+	bool SOUND::DestroyAudio()
497
+	{
498
+		m_bAudioThreadActive = false;
499
+		m_AudioThread.join();
500
+		return false;
501
+	}
502
+
503
+	// Handler for soundcard request for more data
504
+	void CALLBACK SOUND::waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwParam1, DWORD dwParam2)
505
+	{
506
+		if (uMsg != WOM_DONE) return;
507
+		m_nBlockFree++;
508
+		std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
509
+		m_cvBlockNotZero.notify_one();
510
+	}
511
+
512
+	// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
513
+	// with audio data. If no requests are available it goes dormant until the sound
514
+	// card is ready for more data. The block is fille by the "user" in some manner
515
+	// and then issued to the soundcard.
516
+	void SOUND::AudioThread()
517
+	{
518
+		m_fGlobalTime = 0.0f;
519
+		static float fTimeStep = 1.0f / (float)m_nSampleRate;
520
+
521
+		// Goofy hack to get maximum integer for a type at run-time
522
+		short nMaxSample = (short)pow(2, (sizeof(short) * 8) - 1) - 1;
523
+		float fMaxSample = (float)nMaxSample;
524
+		short nPreviousSample = 0;
525
+
526
+		while (m_bAudioThreadActive)
527
+		{
528
+			// Wait for block to become available
529
+			if (m_nBlockFree == 0)
530
+			{
531
+				std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
532
+				while (m_nBlockFree == 0) // sometimes, Windows signals incorrectly
533
+					m_cvBlockNotZero.wait(lm);
534
+			}
535
+
536
+			// Block is here, so use it
537
+			m_nBlockFree--;
538
+
539
+			// Prepare block for processing
540
+			if (m_pWaveHeaders[m_nBlockCurrent].dwFlags & WHDR_PREPARED)
541
+				waveOutUnprepareHeader(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent], sizeof(WAVEHDR));
542
+
543
+			short nNewSample = 0;
544
+			int nCurrentBlock = m_nBlockCurrent * m_nBlockSamples;
545
+
546
+			auto clip = [](float fSample, float fMax)
547
+			{
548
+				if (fSample >= 0.0)
549
+					return fmin(fSample, fMax);
550
+				else
551
+					return fmax(fSample, -fMax);
552
+			};
553
+
554
+			for (unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
555
+			{
556
+				// User Process
557
+				for (unsigned int c = 0; c < m_nChannels; c++)
558
+				{
559
+					nNewSample = (short)(clip(GetMixerOutput(c, m_fGlobalTime, fTimeStep), 1.0) * fMaxSample);
560
+					m_pBlockMemory[nCurrentBlock + n + c] = nNewSample;
561
+					nPreviousSample = nNewSample;
562
+				}
563
+
564
+				m_fGlobalTime = m_fGlobalTime + fTimeStep;
565
+			}
566
+
567
+			// Send block to sound device
568
+			waveOutPrepareHeader(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent], sizeof(WAVEHDR));
569
+			waveOutWrite(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent], sizeof(WAVEHDR));
570
+			m_nBlockCurrent++;
571
+			m_nBlockCurrent %= m_nBlockCount;
572
+		}
573
+	}
574
+
575
+	unsigned int SOUND::m_nSampleRate = 0;
576
+	unsigned int SOUND::m_nChannels = 0;
577
+	unsigned int SOUND::m_nBlockCount = 0;
578
+	unsigned int SOUND::m_nBlockSamples = 0;
579
+	unsigned int SOUND::m_nBlockCurrent = 0;
580
+	short* SOUND::m_pBlockMemory = nullptr;
581
+	WAVEHDR *SOUND::m_pWaveHeaders = nullptr;
582
+	HWAVEOUT SOUND::m_hwDevice;
583
+	std::atomic<unsigned int> SOUND::m_nBlockFree = 0;
584
+	std::condition_variable SOUND::m_cvBlockNotZero;
585
+	std::mutex SOUND::m_muxBlockNotZero;
586
+}
587
+
588
+#elif defined(USE_ALSA)
589
+
590
+namespace olc
591
+{
592
+	bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples)
593
+	{
594
+		// Initialise Sound Engine
595
+		m_bAudioThreadActive = false;
596
+		m_nSampleRate = nSampleRate;
597
+		m_nChannels = nChannels;
598
+		m_nBlockSamples = nBlockSamples;
599
+		m_pBlockMemory = nullptr;
600
+
601
+		// Open PCM stream
602
+		int rc = snd_pcm_open(&m_pPCM, "default", SND_PCM_STREAM_PLAYBACK, 0);
603
+		if (rc < 0)
604
+			return DestroyAudio();
605
+
606
+
607
+		// Prepare the parameter structure and set default parameters
608
+		snd_pcm_hw_params_t *params;
609
+		snd_pcm_hw_params_alloca(&params);
610
+		snd_pcm_hw_params_any(m_pPCM, params);
611
+
612
+		// Set other parameters
613
+		snd_pcm_hw_params_set_format(m_pPCM, params, SND_PCM_FORMAT_S16_LE);
614
+		snd_pcm_hw_params_set_rate(m_pPCM, params, m_nSampleRate, 0);
615
+		snd_pcm_hw_params_set_channels(m_pPCM, params, m_nChannels);
616
+		snd_pcm_hw_params_set_period_size(m_pPCM, params, m_nBlockSamples, 0);
617
+		snd_pcm_hw_params_set_periods(m_pPCM, params, nBlocks, 0);
618
+
619
+		// Save these parameters
620
+		rc = snd_pcm_hw_params(m_pPCM, params);
621
+		if (rc < 0)
622
+			return DestroyAudio();
623
+
624
+		listActiveSamples.clear();
625
+
626
+		// Allocate Wave|Block Memory
627
+		m_pBlockMemory = new short[m_nBlockSamples];
628
+		if (m_pBlockMemory == nullptr)
629
+			return DestroyAudio();
630
+		std::fill(m_pBlockMemory, m_pBlockMemory + m_nBlockSamples, 0);
631
+
632
+		// Unsure if really needed, helped prevent underrun on my setup
633
+		snd_pcm_start(m_pPCM);
634
+		for (unsigned int i = 0; i < nBlocks; i++)
635
+			rc = snd_pcm_writei(m_pPCM, m_pBlockMemory, 512);
636
+
637
+		snd_pcm_start(m_pPCM);
638
+		m_bAudioThreadActive = true;
639
+		m_AudioThread = std::thread(&SOUND::AudioThread);
640
+
641
+		return true;
642
+	}
643
+
644
+	// Stop and clean up audio system
645
+	bool SOUND::DestroyAudio()
646
+	{
647
+		m_bAudioThreadActive = false;
648
+		m_AudioThread.join();
649
+		snd_pcm_drain(m_pPCM);
650
+		snd_pcm_close(m_pPCM);
651
+		return false;
652
+	}
653
+
654
+
655
+	// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
656
+	// with audio data. If no requests are available it goes dormant until the sound
657
+	// card is ready for more data. The block is fille by the "user" in some manner
658
+	// and then issued to the soundcard.
659
+	void SOUND::AudioThread()
660
+	{
661
+		m_fGlobalTime = 0.0f;
662
+		static float fTimeStep = 1.0f / (float)m_nSampleRate;
663
+
664
+		// Goofy hack to get maximum integer for a type at run-time
665
+		short nMaxSample = (short)pow(2, (sizeof(short) * 8) - 1) - 1;
666
+		float fMaxSample = (float)nMaxSample;
667
+		short nPreviousSample = 0;
668
+
669
+		while (m_bAudioThreadActive)
670
+		{
671
+			short nNewSample = 0;
672
+
673
+			auto clip = [](float fSample, float fMax)
674
+			{
675
+				if (fSample >= 0.0)
676
+					return fmin(fSample, fMax);
677
+				else
678
+					return fmax(fSample, -fMax);
679
+			};
680
+
681
+			for (unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
682
+			{
683
+				// User Process
684
+				for (unsigned int c = 0; c < m_nChannels; c++)
685
+				{
686
+					nNewSample = (short)(clip(GetMixerOutput(c, m_fGlobalTime, fTimeStep), 1.0) * fMaxSample);
687
+					m_pBlockMemory[n + c] = nNewSample;
688
+					nPreviousSample = nNewSample;
689
+				}
690
+
691
+				m_fGlobalTime = m_fGlobalTime + fTimeStep;
692
+			}
693
+
694
+			// Send block to sound device
695
+			snd_pcm_uframes_t nLeft = m_nBlockSamples;
696
+			short *pBlockPos = m_pBlockMemory;
697
+			while (nLeft > 0)
698
+			{
699
+				int rc = snd_pcm_writei(m_pPCM, pBlockPos, nLeft);
700
+				if (rc > 0)
701
+				{
702
+					pBlockPos += rc * m_nChannels;
703
+					nLeft -= rc;
704
+				}
705
+				if (rc == -EAGAIN) continue;
706
+				if (rc == -EPIPE) // an underrun occured, prepare the device for more data
707
+					snd_pcm_prepare(m_pPCM);
708
+			}
709
+		}
710
+	}
711
+
712
+	snd_pcm_t* SOUND::m_pPCM = nullptr;
713
+	unsigned int SOUND::m_nSampleRate = 0;
714
+	unsigned int SOUND::m_nChannels = 0;
715
+	unsigned int SOUND::m_nBlockSamples = 0;
716
+	short* SOUND::m_pBlockMemory = nullptr;
717
+}
718
+
719
+#elif defined(USE_OPENAL)
720
+
721
+namespace olc
722
+{
723
+	bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples)
724
+	{
725
+		// Initialise Sound Engine
726
+		m_bAudioThreadActive = false;
727
+		m_nSampleRate = nSampleRate;
728
+		m_nChannels = nChannels;
729
+		m_nBlockCount = nBlocks;
730
+		m_nBlockSamples = nBlockSamples;
731
+		m_pBlockMemory = nullptr;
732
+
733
+		// Open the device and create the context
734
+		m_pDevice = alcOpenDevice(NULL);
735
+		if (m_pDevice)
736
+		{
737
+			m_pContext = alcCreateContext(m_pDevice, NULL);
738
+			alcMakeContextCurrent(m_pContext);
739
+		}
740
+		else
741
+			return DestroyAudio();
742
+
743
+		// Allocate memory for sound data
744
+		alGetError();
745
+		m_pBuffers = new ALuint[m_nBlockCount];
746
+		alGenBuffers(m_nBlockCount, m_pBuffers);
747
+		alGenSources(1, &m_nSource);
748
+
749
+		for (unsigned int i = 0; i < m_nBlockCount; i++)
750
+			m_qAvailableBuffers.push(m_pBuffers[i]);
751
+
752
+		listActiveSamples.clear();
753
+
754
+		// Allocate Wave|Block Memory
755
+		m_pBlockMemory = new short[m_nBlockSamples];
756
+		if (m_pBlockMemory == nullptr)
757
+			return DestroyAudio();
758
+		std::fill(m_pBlockMemory, m_pBlockMemory + m_nBlockSamples, 0);
759
+
760
+		m_bAudioThreadActive = true;
761
+		m_AudioThread = std::thread(&SOUND::AudioThread);
762
+		return true;
763
+	}
764
+
765
+	// Stop and clean up audio system
766
+	bool SOUND::DestroyAudio()
767
+	{
768
+		m_bAudioThreadActive = false;
769
+		m_AudioThread.join();
770
+
771
+		alDeleteBuffers(m_nBlockCount, m_pBuffers);
772
+		delete[] m_pBuffers;
773
+		alDeleteSources(1, &m_nSource);
774
+
775
+		alcMakeContextCurrent(NULL);
776
+		alcDestroyContext(m_pContext);
777
+		alcCloseDevice(m_pDevice);
778
+		return false;
779
+	}
780
+
781
+
782
+	// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
783
+	// with audio data. If no requests are available it goes dormant until the sound
784
+	// card is ready for more data. The block is fille by the "user" in some manner
785
+	// and then issued to the soundcard.
786
+	void SOUND::AudioThread()
787
+	{
788
+		m_fGlobalTime = 0.0f;
789
+		static float fTimeStep = 1.0f / (float)m_nSampleRate;
790
+
791
+		// Goofy hack to get maximum integer for a type at run-time
792
+		short nMaxSample = (short)pow(2, (sizeof(short) * 8) - 1) - 1;
793
+		float fMaxSample = (float)nMaxSample;
794
+		short nPreviousSample = 0;
795
+
796
+		std::vector<ALuint> vProcessed;
797
+
798
+		while (m_bAudioThreadActive)
799
+		{
800
+			ALint nState, nProcessed;
801
+			alGetSourcei(m_nSource, AL_SOURCE_STATE, &nState);
802
+			alGetSourcei(m_nSource, AL_BUFFERS_PROCESSED, &nProcessed);
803
+
804
+			// Add processed buffers to our queue
805
+			vProcessed.resize(nProcessed);
806
+			alSourceUnqueueBuffers(m_nSource, nProcessed, vProcessed.data());
807
+			for (ALint nBuf : vProcessed) m_qAvailableBuffers.push(nBuf);
808
+
809
+			// Wait until there is a free buffer (ewww)
810
+			if (m_qAvailableBuffers.empty()) continue;
811
+
812
+			short nNewSample = 0;
813
+
814
+			auto clip = [](float fSample, float fMax)
815
+			{
816
+				if (fSample >= 0.0)
817
+					return fmin(fSample, fMax);
818
+				else
819
+					return fmax(fSample, -fMax);
820
+			};
821
+
822
+			for (unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
823
+			{
824
+				// User Process
825
+				for (unsigned int c = 0; c < m_nChannels; c++)
826
+				{
827
+					nNewSample = (short)(clip(GetMixerOutput(c, m_fGlobalTime, fTimeStep), 1.0) * fMaxSample);
828
+					m_pBlockMemory[n + c] = nNewSample;
829
+					nPreviousSample = nNewSample;
830
+				}
831
+
832
+				m_fGlobalTime = m_fGlobalTime + fTimeStep;
833
+			}
834
+
835
+			// Fill OpenAL data buffer
836
+			alBufferData(
837
+				m_qAvailableBuffers.front(),
838
+				m_nChannels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
839
+				m_pBlockMemory,
840
+				2 * m_nBlockSamples,
841
+				m_nSampleRate
842
+			);
843
+			// Add it to the OpenAL queue
844
+			alSourceQueueBuffers(m_nSource, 1, &m_qAvailableBuffers.front());
845
+			// Remove it from ours
846
+			m_qAvailableBuffers.pop();
847
+
848
+			// If it's not playing for some reason, change that
849
+			if (nState != AL_PLAYING)
850
+				alSourcePlay(m_nSource);
851
+		}
852
+	}
853
+
854
+	std::queue<ALuint> SOUND::m_qAvailableBuffers;
855
+	ALuint *SOUND::m_pBuffers = nullptr;
856
+	ALuint SOUND::m_nSource = 0;
857
+	ALCdevice *SOUND::m_pDevice = nullptr;
858
+	ALCcontext *SOUND::m_pContext = nullptr;
859
+	unsigned int SOUND::m_nSampleRate = 0;
860
+	unsigned int SOUND::m_nChannels = 0;
861
+	unsigned int SOUND::m_nBlockCount = 0;
862
+	unsigned int SOUND::m_nBlockSamples = 0;
863
+	short* SOUND::m_pBlockMemory = nullptr;
864
+}
865
+
866
+#else // Some other platform
867
+
868
+namespace olc
869
+{
870
+	bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples)
871
+	{
872
+		return true;
873
+	}
874
+
875
+	// Stop and clean up audio system
876
+	bool SOUND::DestroyAudio()
877
+	{
878
+		return false;
879
+	}
880
+
881
+
882
+	// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
883
+	// with audio data. If no requests are available it goes dormant until the sound
884
+	// card is ready for more data. The block is fille by the "user" in some manner
885
+	// and then issued to the soundcard.
886
+	void SOUND::AudioThread()
887
+	{	}
888
+}
889
+
890
+#endif
891
+#endif
892
+#endif // OLC_PGEX_SOUND

Loading…
취소
저장