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