1 module polyplex.core.audio;
2 import polyplex.utils.logging;
3 import openal;
4 public import polyplex.core.audio.soundeffect;
5 public import polyplex.core.audio.music;
6 public import polyplex.core.audio.syncgroup;
7 public import polyplex.core.audio.effect;
8 
9 public enum AudioRenderFormats : int {
10 	/**
11 		Auto detect (only available for PPC Audio streams)
12 	*/
13 	Auto = 0,
14 	Mono8 = AL_FORMAT_MONO8,
15 	Mono16 = AL_FORMAT_MONO16,
16 	MonoFloat = AL_FORMAT_MONO_FLOAT32,
17 
18 	Stereo8 = AL_FORMAT_STEREO8,
19 	Stereo16 = AL_FORMAT_STEREO16,
20 	StereoFloat = AL_FORMAT_STEREO_FLOAT32,
21 }
22 
23 public enum ALExtensionSupport {
24 	/**
25 		Basic OpenAL context is supported.
26 	*/
27 	Basic = 0x00,
28 
29 	/**
30 		EAX 2.0 is supported.
31 	*/
32 	EAX2 = 0x01,
33 
34 	/**
35 		EFX is supported.
36 	*/
37 	EFX = 0x02,
38 
39 	/**
40 		OpenAL-Soft effect chaining
41 	*/
42 	EffectChaining = 0x04
43 	// TODO add more extensions here.
44 }
45 
46 public static AudioDevice DefaultAudioDevice;
47 
48 //TODO: remove this
49 enum ErrCodes : ALCenum {
50 	ALC_FREQUENCY           = 0x1007,
51     ALC_REFRESH             = 0x1008,
52     ALC_SYNC                = 0x1009,
53 
54     ALC_MONO_SOURCES        = 0x1010,
55     ALC_STEREO_SOURCES      = 0x1011,
56 
57     ALC_NO_ERROR            = ALC_FALSE,
58     ALC_INVALID_DEVICE      = 0xA001,
59     ALC_INVALID_CONTEXT     = 0xA002,
60     ALC_INVALID_ENUM        = 0xA003,
61     ALC_INVALID_VALUE       = 0xA004,
62     ALC_OUT_OF_MEMORY       = 0xA005,
63 
64     ALC_DEFAULT_DEVICE_SPECIFIER        = 0x1004,
65     ALC_DEVICE_SPECIFIER                = 0x1005,
66     ALC_EXTENSIONS                      = 0x1006,
67 
68     ALC_MAJOR_VERSION                   = 0x1000,
69     ALC_MINOR_VERSION                   = 0x1001,
70 
71     ALC_ATTRIBUTES_SIZE                 = 0x1002,
72     ALC_ALL_ATTRIBUTES                  = 0x1003,
73 
74     ALC_CAPTURE_DEVICE_SPECIFIER            = 0x310,
75     ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER    = 0x311,
76     ALC_CAPTURE_SAMPLES                     = 0x312,
77 }
78 
79 protected __gshared ALint maxSlots;
80 
81 public class AudioDevice {
82 	private static bool deviceCreationSucceeded;
83 	public ALCdevice* ALDevice;
84 	public ALCcontext* ALContext;
85 	private static ALExtensionSupport supportedExtensions;
86 	public static ALint MixerSize = 10;
87 
88 	public static bool DeviceCreationSucceeded() {
89 		return deviceCreationSucceeded;
90 	}
91 
92 	public static ALExtensionSupport SupportedExtensions() {
93 		return supportedExtensions;
94 	}
95 
96 	/**
97 		Constucts an audio device, NULL for preffered device.
98 	*/
99 	this(string device = null) {
100 		ALint[] attribs = null;
101 
102 		Logger.Info("Initializing OpenAL device...");
103 		ALCdevice* dev = alcOpenDevice(device.ptr);
104 		
105 		// If EAX 2.0 is supported, flag it as supported.
106 		bool supex = cast(bool)alIsExtensionPresent("EAX2.0");
107 		if (supex) {
108 			Logger.Success("EAX 2.0 extensions are supported!");
109 			supportedExtensions |= ALExtensionSupport.EAX2;
110 		}
111 
112 		// If EFX is supported, flag it as supported.
113 		supex = cast(bool)alcIsExtensionPresent(dev, "ALC_EXT_EFX");
114 		if (supex) {
115 			supportedExtensions |= ALExtensionSupport.EFX;
116 			Logger.Success("EFX extensions are supported!");
117 			Logger.Info("Setting up mixer channel count...");
118 			attribs = new ALint[4];
119 			attribs[0] = ALC_MAX_AUXILIARY_SENDS;
120 			attribs[1] = MixerSize;
121 		}
122 
123 		// If Effect Chaining is supported, flag it as supported.
124 		supex = cast(bool)alcIsExtensionPresent(dev, "AL_SOFTX_effect_chain");
125 		if (supex) {
126 			supportedExtensions |= ALExtensionSupport.EffectChaining;
127 			Logger.Success("Effect chains are supported!");
128 		}
129 
130 		if (dev) {
131 			ALContext = alcCreateContext(dev, attribs.ptr);
132 			alcMakeContextCurrent(ALContext);
133 		} else {
134 			import std.conv;
135 			import std.stdio;
136 			Logger.Err("Could not create device! {0}", (cast(ErrCodes)alcGetError(dev)).to!string);
137 			deviceCreationSucceeded = false;
138 			return;
139 		}
140 		if (supex) {
141 			ALint sourceMax;
142 			alcGetIntegerv(dev, ALC_MAX_AUXILIARY_SENDS, 1, &sourceMax);
143 
144 			maxSlots = sourceMax;
145 
146 			Logger.Info("Created {0} mixer sends", sourceMax);
147 		}
148 		deviceCreationSucceeded = true;
149 	}
150 
151 	~this() {
152 		alcMakeContextCurrent(null);
153 		alcDestroyContext(ALContext);
154 		alcCloseDevice(ALDevice);
155 	}
156 
157 	/**
158 		Makes the context for this device, a current context.
159 	*/
160 	public void MakeCurrent() {
161 		alcMakeContextCurrent(ALContext);
162 	}
163 }