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