1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Audio.Sound;
25 
26 private:
27 
28 import derelict.sdl2.mixer;
29 
30 import Dgame.Internal.Error;
31 
32 public:
33 
34 /**
35 * Sound represents the functionality to load and play sound files.
36 * 
37 * Note: Sound is designed to load and play <b>short</b> music files, e.g. sounds for some noises.
38 *       If you want to play larger music, use Music.
39 * Note: Each Sound is played in his own channel, which means, that the number of simultaneously existing Sounds is limited to 256.
40 *       This limit will be increased if necessary.
41 *
42 * Supported formats are .wav, .mp3, .vorbis, .ogg, .midi
43 *
44 * Author: Randy Schuett (rswhite4@googlemail.com)
45 */
46 struct Sound {
47 private:
48     Mix_Chunk* _chunk;
49     ushort _channel;
50 
51     static ushort ChannelCount = 0;
52 
53 public:
54     /**
55      * CTor
56      */
57     @nogc
58     this(string filename, ubyte volume = 128) nothrow {
59         this.loadFromFile(filename);
60         this.setVolume(volume);
61     }
62     
63     /**
64      * Postblit is disabled
65      */
66     @disable
67     this(this);
68 
69     /**
70      * DTor
71      */
72     @nogc
73     ~this() nothrow {
74         Mix_FreeChunk(_chunk);
75     }
76 
77     /**
78      * Returns the current channel in which this Sound plays. Should be in range of 1 .. 256
79      */
80     @nogc
81     ushort getChannel() const pure nothrow {
82         return _channel;
83     }
84 
85     /**
86      * Load the sound file (filename).
87      * Returns if the loading was successful.
88      * If not, an error message is showed which describes what the problem is.
89      */
90     @nogc
91     bool loadFromFile(string filename) nothrow {
92         _chunk = Mix_LoadWAV(filename.ptr);
93         if (!_chunk) {
94             print_fmt("Could not load file: %s\n", Mix_GetError());
95             return false;
96         }
97 
98         return true;
99     }
100 
101     /**
102      * Set the volume, max. is 128, min. is 0
103      * If the value is above 128, the max. will be assumed.
104      * Returns the previous volume.
105      */
106     @nogc
107     ubyte setVolume(ubyte volume) nothrow {
108         if (_chunk)
109             return cast(ubyte) Mix_VolumeChunk(_chunk, volume);
110         return 0;
111     }
112 
113     /**
114      * Returns the current volume.
115      */
116     @nogc
117     ubyte getVolume() nothrow {
118         if (_chunk)
119             return cast(ubyte) Mix_VolumeChunk(_chunk, -1);
120         return 0;
121     }
122 
123     /**
124      * Plays the sound.
125      * loops describe how often the sound shall be played.
126      * A value of -1 indicated, that the sound plays forever,
127      * a value of 0 means, that the sound plays zero times.
128      * delay is the time in ms to fade in.
129      * Any previous sound will be halted.
130      */
131     @nogc
132     bool play(byte loops = 1, short delay = -1) nothrow {
133         if (_chunk) {
134             loops = loops > 0 ? cast(byte)(loops - 1) : loops;
135             Mix_PlayChannelTimed(_channel, _chunk, loops, delay);
136 
137             return true;
138         }
139 
140         return false;
141     }
142 
143     /**
144      * Resume the sound playback
145      *
146      * See: pause
147      * See: stop
148      */
149     @nogc
150     void resume() const nothrow {
151         Mix_Resume(_channel);
152     }
153 
154     /**
155      * Stop/Halt the sound playback
156      *
157      * See: resume
158      */
159     @nogc   
160     void stop() const nothrow {
161         Mix_HaltChannel(_channel);
162     }
163 
164     /**
165      * Pause the sound playback
166      *
167      * See: resume
168      */
169     @nogc
170     void pause() const nothrow {
171         Mix_Pause(_channel);
172     }
173 
174     /**
175      * Stop sound playback after ms milliseconds.
176      */
177     @nogc
178     void expire(ushort ms) const nothrow {
179         Mix_ExpireChannel(_channel, ms);
180     }
181 
182     /**
183      * Returns if the sound is currently playing
184      */
185     @nogc
186     bool isPlaying() const nothrow {
187         return Mix_Playing(_channel) != 0;
188     }
189 
190     /**
191      * Returns if the sound is currently paused
192      */
193     @nogc
194     bool isPaused() const nothrow {
195         return Mix_Paused(_channel) == 0;
196     }
197 }