1 module d2d.audio.music;
2 
3 import d2d;
4 
5 /// 1.0 / MIX_MAX_VOLUME
6 enum INV_MIX_MAX_VOLUME = 1.0f / MIX_MAX_VOLUME;
7 
8 ///
9 enum FadingStatus : ubyte
10 {
11 	///
12 	NoFading = MIX_NO_FADING,
13 	///
14 	FadingOut = MIX_FADING_OUT,
15 	///
16 	FadingIn = MIX_FADING_IN,
17 }
18 
19 ///
20 enum MusicType : ubyte
21 {
22 	///
23 	CommandBased = MUS_CMD,
24 	///
25 	Wav = MUS_WAV,
26 	///
27 	Mod = MUS_MOD,
28 	///
29 	ModPlug = MUS_MODPLUG,
30 	///
31 	Mid = MUS_MID,
32 	///
33 	Ogg = MUS_OGG,
34 	///
35 	Mp3 = MUS_MP3,
36 	///
37 	Mp3Mad = MUS_MP3_MAD,
38 	///
39 	None = MUS_NONE,
40 }
41 
42 /// Thin wrap around Mix_Music including loading [FLAC, MikMod, Ogg Vorbis, MP3, Wav] using SDL_Mixer.
43 class Music : IVerifiable, IDisposable
44 {
45 	private Mix_Music* _handle;
46 
47 	/// Handle to the `Mix_Music*`.
48 	public @property Mix_Music* handle()
49 	{
50 		return _handle;
51 	}
52 
53 	/// Checks if the handle is not null.
54 	public @property bool valid()
55 	{
56 		return _handle !is null;
57 	}
58 
59 	private this(Mix_Music * handle)
60 	{
61 		_handle = handle;
62 	}
63 
64 	/// Loads a music file
65 	public this(string path)
66 	{
67 		_handle = Mix_LoadMUS(path.toStringz());
68 	}
69 
70 	public ~this()
71 	{
72 		dispose();
73 	}
74 
75 	/// Creates music from a `Mix_Music*`.
76 	public static Music fromMusic(Mix_Music* handle)
77 	{
78 		return new Music(handle);
79 	}
80 
81 	public static bool load()
82 	{
83 		return Mix_OpenAudio(44_100, MIX_DEFAULT_FORMAT, 2, 4096) == 0;
84 	}
85 
86 	/// Returns the latest sound related error
87 	public static @property string error()
88 	{
89 		return cast(string) Mix_GetError().fromStringz();
90 	}
91 
92 	/// Deallocates the memory and invalidates `this`.
93 	public void dispose()
94 	{
95 		if (valid)
96 		{
97 			stop();
98 			Mix_FreeMusic(_handle);
99 			_handle = null;
100 		}
101 	}
102 
103 	/// Plays this music, will stop other musics. -1 loops will play forever.
104 	/// Returns: true on success, or false on errors
105 	public bool play(int loops = 1)
106 	{
107 		return Mix_PlayMusic(_handle, loops) == 0;
108 	}
109 
110 	/// Starts this music by fading in over `ms` milliseconds, will stop other musics. -1 loops will play forever.
111 	/// Params:
112 	///     ms = Milliseconds for the fade-in effect to complete.
113 	///     loops = -1 loops will play forever. 0 plays the music zero times.
114 	/// Returns: true on success, or false on errors
115 	public bool fadeIn(int ms, int loops = 1)
116 	{
117 		return Mix_FadeInMusic(_handle, loops, ms) == 0;
118 	}
119 
120 	/// Starts this music by fading in over `ms` milliseconds, will stop other musics.
121 	/// Params:
122 	///     ms = Milliseconds for the fade-in effect to complete.
123 	///     loops = -1 loops will play forever. 0 plays the music zero times.
124 	///     position = Set the position of the currently playing music. The position takes different meanings for different music sources. It only works on the music sources listed below.
125 	///         <b>MOD</b>
126 	///             The double is cast to Uint16 and used for a pattern number in the module. Passing zero is similar to rewinding the song.
127 	///         <b>OGG/MP3</b>
128 	///             Jumps to position seconds from the beginning of the song.
129 	/// Returns: true on success, or false on errors
130 	public bool fadeIn(int ms, int loops, double position)
131 	{
132 		return Mix_FadeInMusicPos(_handle, loops, ms, position) == 0;
133 	}
134 
135 	///
136 	public @property MusicType type()
137 	{
138 		return cast(MusicType) Mix_GetMusicType(_handle);
139 	}
140 
141 	/// Modifies the position where currently playing.
142 	/// Params:
143 	///     value = Set the position of the currently playing music. The position takes different meanings for different music sources. It only works on the music sources listed below. Will automatically rewind before setting.
144 	///         <b>MOD</b>
145 	///             The double is cast to Uint16 and used for a pattern number in the module. Passing zero is similar to rewinding the song.
146 	///         <b>OGG/MP3</b>
147 	///             Jumps to position seconds from the beginning of the song.
148 	/// Returns: true on success, or false if the codec doesn't support this function
149 	public static @property bool position(double value)
150 	{
151 		Mix_RewindMusic();
152 		return Mix_SetMusicPosition(value) == 0;
153 	}
154 
155 	/// Rewinds music to the start. This is safe to use on halted, paused, and already playing music. It is not useful to rewind the music immediately after starting playback, because it starts at the beginning by default.
156 	public static void rewind()
157 	{
158 		Mix_RewindMusic();
159 	}
160 
161 	/// Halt playback of music. This interrupts music fader effects.
162 	public static void stop()
163 	{
164 		Mix_RewindMusic();
165 	}
166 
167 	/// Pause the music playback. You may halt paused music.
168 	/// Note: Music can only be paused if it is actively playing.
169 	public static void pause()
170 	{
171 		Mix_PauseMusic();
172 	}
173 
174 	/// Unpause the music. This is safe to use on halted, paused, and already playing music.
175 	public static void resume()
176 	{
177 		Mix_ResumeMusic();
178 	}
179 
180 	/// Returns the current volume as float in range 0.0 to 1.0
181 	public static @property float volume()
182 	{
183 		return Mix_VolumeMusic(-1) * INV_MIX_MAX_VOLUME;
184 	}
185 
186 	/// Sets the current volume as float in range 0.0 to 1.0
187 	public static @property void volume(float value)
188 	{
189 		Mix_VolumeMusic(cast(int) (value * MIX_MAX_VOLUME));
190 	}
191 
192 	///
193 	public static @property bool isPlaying()
194 	{
195 		return Mix_PlayingMusic() == 1;
196 	}
197 
198 	///
199 	public static @property bool isPaused()
200 	{
201 		return Mix_PausedMusic() == 1;
202 	}
203 
204 	///
205 	public static @property FadingStatus fadingStatus()
206 	{
207 		return cast(FadingStatus) Mix_FadingMusic();
208 	}
209 }