1 /*
2  *  zlib License
3  *  
4  *  (C) 2016 jython234
5  *  
6  *  This software is provided 'as-is', without any express or implied
7  *  warranty.  In no event will the authors be held liable for any damages
8  *  arising from the use of this software.
9  *  
10  *  Permission is granted to anyone to use this software for any purpose,
11  *  including commercial applications, and to alter it and redistribute it
12  *  freely, subject to the following restrictions:
13  *  
14  *  1. The origin of this software must not be misrepresented; you must not
15  *     claim that you wrote the original software. If you use this software
16  *     in a product, an acknowledgment in the product documentation would be
17  *     appreciated but is not required.
18  *  2. Altered source versions must be plainly marked as such, and must not be
19  *     misrepresented as being the original software.
20  *  3. This notice may not be removed or altered from any source distribution.
21 */
22 module blocksound.audio;
23 
24 import blocksound.core;
25 import blocksound.backend.types;
26 
27 public import blocksound.backend.types : Source, Sound, StreamingSource, StreamedSound;
28 
29 version(blocksound_ALBackend) {
30     import blocksound.backend.openal;
31 }
32 
33 class Lock {
34 
35 }
36 
37 /++
38     Loads a Sound from a file.
39 
40     Params:
41             file =  The file where the sound is stored.
42 
43     Returns: A Sound instance loaded from the specified file.
44 +/
45 Sound loadSoundFile(in string file) @trusted {
46     version(blocksound_ALBackend) {
47         import blocksound.backend.openal : ALSound;
48 
49         return ALSound.loadSound(file);
50     } else {
51         throw new Exception("No backend avaliable! (Try compiling with version \"blocksound_ALBackend\" enabled)");
52     }
53 }
54 
55 /++
56     Loads a Sound from a file for streaming.
57 
58     Params:
59             file =  The file where the sound is stored.
60 
61     Returns: A StreamedSound instance loaded from the specified file.
62 +/
63 StreamedSound loadStreamingSoundFile(in string file, in size_t numBuffers = 4) @trusted {
64     version(blocksound_ALBackend) {
65         import blocksound.backend.openal : ALStreamedSound;
66         import derelict.openal.al : ALuint;
67 
68         return ALStreamedSound.loadSound(file, cast(ALuint) numBuffers);
69     } else {
70         throw new Exception("No backend avaliable! (Try compiling with version \"blocksound_ALBackend\" enabled)");
71     }
72 }
73 
74 /// Manages the Audio.
75 class AudioManager {
76     private shared Lock listenerLock;
77     private shared Lock gainLock;
78 
79     private shared Vec3 _listenerLocation;
80     private shared float _gain;
81 
82     private AudioBackend backend;
83     private shared ArrayList!Source sources;
84 
85     /// The location where the listener is.
86     @property Vec3 listenerLocation() @trusted {
87         synchronized(listenerLock) { 
88             return cast(Vec3) _listenerLocation;
89         } 
90     }
91     /// The location where the listener is.
92     @property void listenerLocation(Vec3 loc) @safe {
93         synchronized(listenerLock) {
94             _listenerLocation = cast(shared) loc; 
95             backend.setListenerLocation(loc);
96         } 
97     }
98 
99     /// The listener's gain or volume.
100     @property float gain() @trusted {
101         synchronized(gainLock) { 
102             return cast(shared) _gain;
103         } 
104     }
105     /// The listener's gain or volume.
106     @property void gain(float gain) @safe {
107         synchronized(gainLock) {
108             _gain = cast(shared) gain;
109             backend.setListenerGain(gain);
110         }
111     }
112 
113     /++
114         Initializes the AudioManager and it's backend.
115         Backend is decided at compile-time.
116     +/
117     this() @trusted {
118         import std.exception : enforce;
119         
120         enforce(INIT, new Exception("BlockSound has not been initialized!"));
121 
122         version(blocksound_ALBackend) {
123             backend = new ALAudioBackend();
124         } else {
125             throw new Exception("No backend avaliable! (Try compiling with version \"blocksound_ALBackend\" enabled)");
126         }
127 
128         listenerLock = cast(shared) new Lock();
129         gainLock = cast(shared) new Lock();
130 
131         sources = new ArrayList!Source();
132     }
133 
134     /++
135         Create a a new Source at the specified
136         location. The Source is also added to this AudioManager.
137 
138         Params:
139                 location =  The location of the Source.
140 
141         Returns: A new Source.
142     +/
143     Source createSource(Vec3 location) @trusted {
144         Source source = backend_createSource(location);
145         sources.add(source);
146         return source;
147     }
148 
149     /++
150         Create a a new StreamingSource at the specified
151         location. The Source is also added to this AudioManager.
152         This is for Streaming sounds.
153 
154         Params:
155                 location =  The location of the Source.
156 
157         Returns: A new Source.
158     +/
159     StreamingSource createStreamingSource(Vec3 location) @trusted {
160         StreamingSource source = backend_createStreamingSource(location);
161         sources.add(source);
162         return source;
163     }
164 
165     /++
166         Deletes a Source, frees it's resources,
167         and removes it from the AudioManager.
168 
169         Params:
170                 source =    The source to be deleted.
171     +/
172     void deleteSource(Source source) @trusted {
173         sources.remove(source);
174         source.cleanup();
175     }
176 
177     /// Cleanup any resources used by the backend.
178     void cleanup() @trusted {
179         backend.cleanup();
180     }
181 }