1 module d2d.audio.sound;
2 
3 import d2d;
4 
5 /// Thin wrap around Mix_Chunk including loading [FLAC, MikMod, Ogg Vorbis, MP3, Wav] using SDL_Mixer.
6 class Sound : IVerifiable, IDisposable
7 {
8 	private Mix_Chunk* _handle;
9 
10 	/// Handle to the `Mix_Chunk*`.
11 	public @property Mix_Chunk* handle()
12 	{
13 		return _handle;
14 	}
15 
16 	/// Checks if the handle is not null.
17 	public @property bool valid()
18 	{
19 		return _handle !is null;
20 	}
21 
22 	private this(Mix_Chunk * handle)
23 	{
24 		_handle = handle;
25 	}
26 
27 	/// Loads a sound file
28 	public this(string path)
29 	{
30 		// Confusing name, this can load any format
31 		_handle = Mix_LoadWAV(path.toStringz());
32 	}
33 
34 	public ~this()
35 	{
36 		dispose();
37 	}
38 
39 	/// Creates sound from a `Mix_Chunk*`.
40 	public static Sound fromChunk(Mix_Chunk* chunk)
41 	{
42 		return new Sound(chunk);
43 	}
44 
45 	/// Returns the latest sound related error
46 	public static @property string error()
47 	{
48 		return cast(string) Mix_GetError().fromStringz();
49 	}
50 
51 	/// Deallocates the memory and invalidates `this`.
52 	public void dispose()
53 	{
54 		if (valid)
55 		{
56 			stop();
57 			Mix_FreeChunk(_handle);
58 			_handle = null;
59 		}
60 	}
61 
62 	/// Plays this sound in the selected channel.
63 	/// Params:
64 	///     loops = -1 loops will play forever. 0 plays once.
65 	///     channel = -1 loops will choose the first free unreserved channel.
66 	///     maxTicks = Maximum milliseconds to play this sound. If not enough loops or the sample chunk is not long enough, then the sample may stop before this timeout occurs. -1 means play forever.
67 	/// Returns: the channel the sample is played on. On any errors, -1 is returned.
68 	public int play(int loops = 0, int channel = -1, int maxTicks = -1)
69 	{
70 		return Mix_PlayChannelTimed(channel, _handle, loops, maxTicks);
71 	}
72 
73 	/// Plays this sound in the selected channel with a fade in effect.
74 	/// Params:
75 	///     ms = Milliseconds for the fade-in effect to complete.
76 	///     loops = -1 loops will play forever. 0 plays once.
77 	///     channel = -1 loops will choose the first free unreserved channel.
78 	///     maxTicks = Maximum milliseconds to play this sound. If not enough loops or the sample chunk is not long enough, then the sample may stop before this timeout occurs. -1 means play forever.
79 	/// Returns: the channel the sample is played on. On any errors, -1 is returned.
80 	public int fadeIn(int ms, int loops = 1, int channel = -1, int maxTicks = -1)
81 	{
82 		return Mix_FadeInChannelTimed(channel, _handle, loops, ms, maxTicks);
83 	}
84 
85 	/// Halt playback of sound. This interrupts sound fader effects.
86 	/// Params:
87 	///     channel = Channel to stop playing, or -1 for all channels.
88 	public static void stop(int channel = -1)
89 	{
90 		Mix_HaltChannel(channel);
91 	}
92 
93 	/// Pause the sound playback. You may halt paused sounds.
94 	/// Params:
95 	///     channel = Channel to stop playing, or -1 for all channels.
96 	/// Note: Sound can only be paused if it is actively playing.
97 	public static void pause(int channel = -1)
98 	{
99 		Mix_Pause(channel);
100 	}
101 
102 	/// Unpause the sound. This is safe to use on halted, paused, and already playing sounds.
103 	/// Params:
104 	///     channel = Channel to stop playing, or -1 for all channels.
105 	public static void resume(int channel = -1)
106 	{
107 		Mix_Resume(channel);
108 	}
109 
110 	/// Returns the current volume as float in range 0.0 to 1.0
111 	/// Params:
112 	///     channel = -1 for all channels.
113 	public static float getVolume(int channel = -1)
114 	{
115 		return Mix_Volume(channel, -1) * INV_MIX_MAX_VOLUME;
116 	}
117 
118 	/// Sets the current volume as float in range 0.0 to 1.0
119 	/// Params:
120 	///     value = volume between 0.0 and 1.0.
121 	///     channel = -1 for all channels.
122 	public static void setVolume(float value, int channel = -1)
123 	{
124 		Mix_Volume(channel, cast(int) (value * MIX_MAX_VOLUME));
125 	}
126 
127 	/// Tells you how many channels are playing if set to -1
128 	/// Params:
129 	///     channel = -1 for all channels.
130 	public static @property int isPlaying(int channel = -1)
131 	{
132 		return Mix_Playing(channel);
133 	}
134 
135 	/// Tells you how many channels are paused if set to -1
136 	/// Params:
137 	///     channel = -1 for all channels.
138 	public static @property int isPaused(int channel = -1)
139 	{
140 		return Mix_Paused(channel);
141 	}
142 
143 	///
144 	/// Params:
145 	///     channel = -1 is invalid.
146 	public static @property FadingStatus fadingStatus(int channel)
147 	{
148 		assert(channel != -1);
149 		return cast(FadingStatus) Mix_FadingChannel(channel);
150 	}
151 }