1 /++ 2 + Authors: Avaxar <avaxar@nekkl.org> 3 + Copyright: Copyright © 2023, Avaxar 4 + License: $(LINK2 https://mit-license.org, MIT License) 5 +/ 6 7 module dsdl2.mixer; 8 @safe: 9 10 // dfmt off 11 import bindbc.sdl; 12 static if (bindSDLMixer): 13 // dfmt on 14 15 import dsdl2.sdl; 16 import dsdl2.audio; 17 18 import core.memory : GC; 19 import std.conv : to; 20 import std.format : format; 21 import std.string : toStringz; 22 import std.typecons : Tuple; 23 24 version (BindSDL_Static) { 25 } 26 else { 27 /++ 28 + Loads the SDL2_mixer shared dynamic library, which wraps bindbc-sdl's `loadSDLMixer` function 29 + 30 + Unless if bindbc-sdl is on static mode (by adding a `BindSDL_Static` version), this function will exist and must 31 + be called before any calls are made to the library. Otherwise, a segfault will happen upon any function calls. 32 + 33 + Params: 34 + libName = name or path to look the SDL2_mixer SO/DLL for, otherwise `null` for default searching path 35 + Throws: `dsdl2.SDLException` if failed to find the library 36 +/ 37 void loadSO(string libName = null) @trusted { 38 SDLMixerSupport current = libName is null ? loadSDLMixer() : loadSDLMixer(libName.toStringz()); 39 if (current == sdlMixerSupport) { 40 return; 41 } 42 43 Version wanted = Version(sdlMixerSupport); 44 if (current == SDLMixerSupport.badLibrary) { 45 import std.stdio : writeln; 46 47 writeln("WARNING: dsdl2 expects SDL_mixer ", wanted.format(), ", but got ", getVersion().format(), "."); 48 } 49 else if (current == SDLMixerSupport.noLibrary) { 50 throw new SDLException("No SDL2_mixer library found, especially of version " ~ wanted.format(), 51 __FILE__, __LINE__); 52 } 53 } 54 } 55 56 /++ 57 + Wraps `Mix_Init` which initializes selected SDL2_mixer audio format subsystems 58 + 59 + Params: 60 + flac = selects the `MIX_INIT_FLAC` subsystem 61 + mod = selects the `MIX_INIT_MOD` subsystem 62 + mp3 = selects the `MIX_INIT_MP3` subsystem 63 + ogg = selects the `MIX_INIT_OGG` subsystem 64 + mid = selects the `MIX_INIT_FLUIDSYNTH` (for SDL_mixer below 2.0.2) / `MIX_INIT_MID` subsystem 65 + opus = selects the `MIX_INIT_OPUS` subsystem (from SDL_mixer 2.0.4) 66 + everything = selects every available subsystem 67 + Throws: `dsdl2.SDLException` if any selected subsystem failed to initialize 68 + Example: 69 + --- 70 + dsdl2.mixer.init(everything : true); 71 + --- 72 +/ 73 void init(bool flac = false, bool mod = false, bool mp3 = false, bool ogg = false, bool mid = false, 74 bool opus = false, bool everything = false) @trusted 75 in { 76 static if (sdlMixerSupport < SDLMixerSupport.v2_0_4) { 77 assert(opus == false); 78 } 79 else { 80 if (opus) { 81 assert(dsdl2.mixer.getVersion() >= Version(2, 0, 4)); 82 } 83 } 84 } 85 do { 86 int flags = 0; 87 88 flags |= flac ? MIX_INIT_FLAC : 0; 89 flags |= mod ? MIX_INIT_MOD : 0; 90 flags |= mp3 ? MIX_INIT_MP3 : 0; 91 flags |= ogg ? MIX_INIT_OGG : 0; 92 flags |= everything ? MIX_INIT_FLAC | MIX_INIT_MOD | MIX_INIT_MP3 | MIX_INIT_OGG : 0; 93 94 static if (sdlMixerSupport >= SDLMixerSupport.v2_0_2) { 95 flags |= mid ? MIX_INIT_MID : 0; 96 flags |= everything ? MIX_INIT_MID : 0; 97 } 98 else { 99 flags |= mid ? MIX_INIT_FLUIDSYNTH : 0; 100 flags |= everything ? MIX_INIT_FLUIDSYNTH : 0; 101 } 102 103 static if (sdlMixerSupport >= SDLMixerSupport.v2_0_4) { 104 flags |= opus ? MIX_INIT_OPUS : 0; 105 flags |= everything ? MIX_INIT_OPUS : 0; 106 } 107 108 if ((Mix_Init(flags) & flags) != flags) { 109 throw new SDLException; 110 } 111 } 112 113 version (unittest) { 114 static this() { 115 version (BindSDL_Static) { 116 } 117 else { 118 dsdl2.mixer.loadSO(); 119 } 120 121 dsdl2.mixer.init(everything : true); 122 } 123 } 124 125 /++ 126 + Wraps `Mix_Quit` which entirely deinitializes SDL2_mixer 127 +/ 128 void quit() @trusted { 129 Mix_Quit(); 130 } 131 132 version (unittest) { 133 static ~this() { 134 dsdl2.mixer.quit(); 135 } 136 } 137 138 /++ 139 + Wraps `Mix_Linked_Version` which gets the version of the linked SDL2_mixer library 140 + 141 + Returns: `dsdl2.Version` of the linked SDL2_mixer library 142 +/ 143 Version getVersion() @trusted { 144 return Version(*Mix_Linked_Version()); 145 } 146 147 enum channels = cast(ubyte) MIX_CHANNELS; /// Alias to `MIX_CHANNELS` 148 enum defaultFrequency = cast(uint) MIX_DEFAULT_FREQUENCY; /// Alias to `MIX_DEFAULT_FREQUENCY` 149 enum defaultFormat = cast(AudioFormat) MIX_DEFAULT_FORMAT; /// Alias to `MIX_DEFAULT_FORMAT` 150 enum maxVolume = cast(ubyte) MIX_MAX_VOLUME; /// Alias to `MIX_MAX_VOLUME` 151 152 /++ 153 + D enum that wraps `Mix_Fading` 154 +/ 155 enum Fading { 156 /++ 157 + Wraps `MIX_*` enumeration constants 158 +/ 159 noFading = MIX_NO_FADING, 160 fadingIn = MIX_FADING_IN, /// ditto 161 fadingOut = MIX_FADING_OUT /// ditto 162 } 163 164 /++ 165 + D enum that wraps `Mix_MusicType` 166 +/ 167 enum MusicType { 168 /++ 169 + Wraps `MUS_*` enumeration constants 170 +/ 171 none = MUS_NONE, 172 cmd = MUS_CMD, /// ditto 173 wav = MUS_WAV, /// ditto 174 mod = MUS_MOD, /// ditto 175 mid = MUS_MID, /// ditto 176 ogg = MUS_OGG, /// ditto 177 mp3 = MUS_MP3, /// ditto 178 flac = MUS_FLAC, /// ditto 179 180 opus = 10 /// Wraps `MUS_OPUS` (from SDL_mixer 2.0.4) 181 } 182 183 /++ 184 + Wraps `Mix_OpenAudio` which opens the default audio device for playback by SDL_mixer 185 + 186 + Params: 187 + frequency = audio playback frequency in Hz 188 + format = `dsdl2.AudioFormat` enumeration indicating the scalar type of each audio sample 189 + channels = channels for `dsdl2.mixer.Chunk` playback (1 for mono; 2 for stereo) 190 + chunkSize = audio buffer size 191 + Throws: `dsdl2.SDLException` if failed to open default audio device 192 +/ 193 void openAudio(uint frequency, AudioFormat format, uint channels, uint chunkSize) @trusted { 194 if (Mix_OpenAudio(frequency.to!int, cast(ushort) format, channels.to!int, chunkSize.to!int) != 0) { 195 throw new SDLException; 196 } 197 } 198 199 static if (sdlMixerSupport >= SDLMixerSupport.v2_0_2) { 200 /++ 201 + Wraps `Mix_OpenAudioDevice` (from SDL_mixer 2.0.2) which opens a selected audio device for playback by SDL_mixer 202 + 203 + Params: 204 + frequency = audio playback frequency in Hz 205 + format = `dsdl2.AudioFormat` enumeration indicating the scalar type of each audio sample 206 + channels = channels for `dsdl2.mixer.Chunk` playback (1 for mono; 2 for stereo) 207 + chunkSize = audio buffer size 208 + deviceName = name of the selected device 209 + allowFrequencyChange = adds `SDL_AUDIO_ALLOW_FREQUENCY_CHANGE` flag 210 + allowFormatChange = adds `SDL_AUDIO_ALLOW_FORMAT_CHANGE` flag 211 + allowChannelsChange = adds `SDL_AUDIO_ALLOW_CHANNELS_CHANGE` flag 212 + allowSamplesChange = adds `SDL_AUDIO_ALLOW_SAMPLES_CHANGE` flag (from SDL_mixer 2.0.9) 213 + allowAnyChange = adds `SDL_AUDIO_ALLOW_ANY_CHANGE` flag 214 + Throws: `dsdl2.SDLException` if failed to open the selected audio device 215 +/ 216 void openAudioDevice(uint frequency, AudioFormat format, uint channels, uint chunkSize, string deviceName, 217 bool allowFrequencyChange = false, bool allowFormatChange = false, bool allowChannelsChange = false, 218 bool allowSamplesChange = false, bool allowAnyChange = false) @trusted 219 in { 220 assert(dsdl2.mixer.getVersion() >= Version(2, 0, 2)); 221 222 static if (sdlSupport < SDLSupport.v2_0_9) { 223 assert(allowSamplesChange == false); 224 } 225 else { 226 if (allowSamplesChange) { 227 assert(dsdl2.mixer.getVersion() >= Version(2, 0, 9)); 228 } 229 } 230 } 231 do { 232 int changeFlags = 0; 233 changeFlags |= allowFrequencyChange ? SDL_AUDIO_ALLOW_FREQUENCY_CHANGE : 0; 234 changeFlags |= allowFormatChange ? SDL_AUDIO_ALLOW_FORMAT_CHANGE : 0; 235 changeFlags |= allowChannelsChange ? SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0; 236 changeFlags |= allowAnyChange ? SDL_AUDIO_ALLOW_ANY_CHANGE : 0; 237 238 static if (sdlSupport >= SDLSupport.v2_0_9) { 239 changeFlags |= allowSamplesChange ? SDL_AUDIO_ALLOW_SAMPLES_CHANGE : 0; 240 } 241 242 if (Mix_OpenAudioDevice(frequency.to!int, cast(ushort) format, channels.to!int, chunkSize.to!int, 243 deviceName.toStringz(), changeFlags) != 0) { 244 throw new SDLException; 245 } 246 } 247 } 248 249 /++ 250 + Wraps `Mix_AllocateChannels` which changes the amount of track channels managed by SDL_mixer 251 + 252 + Params: 253 + channels = new amount of track channels (not the same as device channels) 254 +/ 255 void allocateChannels(uint channels) @trusted { 256 Mix_AllocateChannels(channels.to!int); 257 } 258 259 /++ 260 + Wraps `Mix_ReserveChannels` which reserves the first track channels managed by SDL_mixer for the application 261 + 262 + Params: 263 + channels = amount of the first track channels to reserve (not the same as device channels) 264 +/ 265 void reserveChannels(uint channels) @trusted { 266 Mix_ReserveChannels(channels.to!int); 267 } 268 269 private alias MixerSpec = Tuple!(uint, "frequency", AudioFormat, "format", uint, "channels"); 270 271 /++ 272 + Wraps `Mix_QuerySpec` which queries the default audio device information 273 + 274 + Returns: a named tuple detailing the default audio device's `frequency`, audio `format`, and `channels` 275 + Throws: `dsdl2.SDLException` if failed to query the information 276 +/ 277 MixerSpec querySpec() @trusted { 278 MixerSpec spec = void; 279 if (Mix_QuerySpec(cast(int*)&spec.frequency, cast(ushort*)&spec.format, cast(int*)&spec.channels) != 0) { 280 throw new SDLException; 281 } 282 283 return spec; 284 } 285 286 static if (sdlMixerSupport >= SDLMixerSupport.v2_6) { 287 /++ 288 + Wraps `Mix_MasterVolume` (from SDL_mixer 2.6) which gets the master volume 289 + 290 + Returns: master volume ranging from `0` to `128` 291 +/ 292 ubyte getMasterVolume() @trusted { 293 return cast(ubyte) Mix_MasterVolume(-1); 294 } 295 296 /++ 297 + Wraps `Mix_MasterVolume` (from SDL_mixer 2.6) which sets the master volume 298 + 299 + Params: 300 + newVolume = new master volume ranging from `0` to `128` 301 +/ 302 void setMasterVolume(ubyte newVolume) @trusted { 303 Mix_MasterVolume(newVolume); 304 } 305 } 306 307 /++ 308 + D class that acts as a proxy for a mixer audio channel from a channel ID 309 +/ 310 final class Channel { 311 const uint mixChannel; /// Channel ID from SDL_mixer 312 313 this() @disable; 314 315 private this(uint mixChannel) { 316 this.mixChannel = mixChannel; 317 } 318 319 /++ 320 + Equality operator overload 321 +/ 322 bool opEquals(const Channel rhs) const { 323 return this.mixChannel == rhs.mixChannel; 324 } 325 326 /++ 327 + Gets the hash of the `dsdl2.mixer.Channel` 328 + 329 + Returns: unique hash for the instance being the channel ID 330 +/ 331 override hash_t toHash() const { 332 return cast(hash_t) this.mixChannel; 333 } 334 335 /++ 336 + Formats the `dsdl2.mixer.Channel` showing its internal information: `"dsdl2.mixer.Channel(<mixChannel>)"` 337 + 338 + Returns: the formatted `string` 339 +/ 340 override string toString() const { 341 return "dsdl2.mixer.Channel(%d)".format(this.mixChannel); 342 } 343 344 /++ 345 + Wraps `Mix_SetPanning` which sets the panning volume of the left and right channels for the track channel 346 + 347 + Params: 348 + newLR = tuple of the volumes from `0` to `255` of the left and right channels in order 349 + Throws: `dsdl2.SDLException` if failed to set the panning 350 +/ 351 void panning(ubyte[2] newLR) const @property @trusted { 352 if (Mix_SetPanning(this.mixChannel, newLR[0], newLR[1]) == 0) { 353 throw new SDLException; 354 } 355 } 356 357 /++ 358 + Wraps `Mix_SetPanning` which sets the panning volume of the left and right channels as posteffect for all 359 + channels 360 + 361 + Params: 362 + newLR = tuple of the volumes from `0` to `255` of the left and right channels in order 363 + Throws: `dsdl2.SDLException` if failed to set the panning 364 +/ 365 static void allPanning(ubyte[2] newLR) @property @trusted { 366 if (Mix_SetPanning(MIX_CHANNEL_POST, newLR[0], newLR[1]) == 0) { 367 throw new SDLException; 368 } 369 } 370 371 /++ 372 + Wraps `Mix_SetPosition` which sets the simulated angle and distance of the channel playing from the listener 373 + 374 + Params: 375 + newAngleDistance = tuple of the simulated angle (in degrees clockwise, with `0` being the front) and distance 376 + Throws: `dsdl2.SDLException` if failed to set the position 377 +/ 378 void position(Tuple!(short, ubyte) newAngleDistance) const @property @trusted { 379 if (Mix_SetPosition(this.mixChannel, newAngleDistance[0], newAngleDistance[1]) == 0) { 380 throw new SDLException; 381 } 382 } 383 384 /++ 385 + Acts as `Mix_SetPosition(MIX_CHANNEL_POST, newAngleDistance[0], newAngleDistance[1])` which sets the simulated 386 + angle and distance for all channels as posteffect 387 + 388 + Params: 389 + newAngleDistance = tuple of the simulated angle (in degrees clockwise, with `0` being the front) and distance 390 + Throws: `dsdl2.SDLException` if failed to set the position 391 +/ 392 static void allPosition(Tuple!(short, ubyte) newAngleDistance) @property @trusted { 393 if (Mix_SetPosition(MIX_CHANNEL_POST, newAngleDistance[0], newAngleDistance[1]) == 0) { 394 throw new SDLException; 395 } 396 } 397 398 /++ 399 + Wraps `Mix_SetDistance` which sets the simulated distance for the channel playing from the listener 400 + 401 + Params: 402 + newDistance = simulated distance from `0` to `255` 403 + Throws: `dsdl2.SDLException` if failed to set the distance 404 +/ 405 void distance(ubyte newDistance) const @property @trusted { 406 if (Mix_SetDistance(this.mixChannel, newDistance) == 0) { 407 throw new SDLException; 408 } 409 } 410 411 /++ 412 + Acts as `Mix_SetDistance(MIX_CHANNEL_POST, newDistance)` which sets the simulated distance for all channels as 413 + posteffect 414 + 415 + Params: 416 + newDistance = simulated distance from `0` to `255` 417 + Throws: `dsdl2.SDLException` if failed to set the distance 418 +/ 419 static void allDistance(ubyte newDistance) @property @trusted { 420 if (Mix_SetDistance(MIX_CHANNEL_POST, newDistance) == 0) { 421 throw new SDLException; 422 } 423 } 424 425 /++ 426 + Wraps `Mix_Volume` which gets the volume of the channel 427 + 428 + Returns: volume ranging from `0` to `128` 429 +/ 430 ubyte volume() const @property @trusted { 431 return cast(ubyte) Mix_Volume(this.mixChannel, -1); 432 } 433 434 /++ 435 + Acts as `Mix_Volume(-1, -1)` which gets the volume of all channels 436 + 437 + Returns: volume ranging from `0` to `128` 438 +/ 439 static ubyte allVolume() @property @trusted { 440 return cast(ubyte) Mix_Volume(-1, -1); 441 } 442 443 /++ 444 + Wraps `Mix_Volume` which sets the volume of the channel 445 + 446 + Params: 447 + newVolume = new volume ranging from `0` to `128` 448 +/ 449 void volume(ubyte newVolume) const @property @trusted { 450 Mix_Volume(this.mixChannel, newVolume); 451 } 452 453 /++ 454 + Acts as `Mix_Volume(-1, newVolume)` which sets the volume of all channels 455 + 456 + Params: 457 + newVolume = new volume ranging from `0` to `128` 458 +/ 459 static void allVolume(ubyte newVolume) @property @trusted { 460 Mix_Volume(-1, newVolume); 461 } 462 463 /++ 464 + Wraps `Mix_SetReverseStereo` which sets whether the left and right channels are flipped in the channel 465 + 466 + Params: 467 + newReverse = `true` to enable reverse stereo; `false` to disable 468 + Throws: `dsdl2.SDLException` if failed to set the reverse stereo mode 469 +/ 470 void reverseStereo(bool newReverse) const @property @trusted { 471 if (Mix_SetReverseStereo(this.mixChannel, newReverse ? 1 : 0) == 0) { 472 throw new SDLException; 473 } 474 } 475 476 /++ 477 + Acts as `Mix_SetReverseStereo(MIX_CHANNEL_POST, newReverse)` which sets whether the left and right channels are 478 + flipped for all channels as posteffect 479 + 480 + Params: 481 + newReverse = `true` to enable reverse stereo; `false` to disable 482 + Throws: `dsdl2.SDLException` if failed to set the reverse stereo mode 483 +/ 484 static void allReverseStereo(bool newReverse) @property @trusted { 485 if (Mix_SetReverseStereo(MIX_CHANNEL_POST, newReverse ? 1 : 0) == 0) { 486 throw new SDLException; 487 } 488 } 489 490 /++ 491 + Wraps `Mix_Paused` which checks whether the channel is paused 492 + 493 + Returns: `true` if channel is paused, otherwise `false` 494 +/ 495 bool paused() const @property @trusted { 496 return Mix_Paused(this.mixChannel) == 1; 497 } 498 499 /++ 500 + Wraps `Mix_Playing` which checks whether the channel is playing 501 + 502 + Returns: `true` if channel is playing, otherwise `false` 503 +/ 504 bool playing() const @property @trusted { 505 return Mix_Playing(this.mixChannel) == 1; 506 } 507 508 /++ 509 + Wraps `Mix_FadingChannel` which gets the fading stage of the channel 510 + 511 + Returns: `dsdl2.mixer.Fading` enumeration indicating the channel's fading stage 512 +/ 513 Fading fading() const @property @trusted { 514 return cast(Fading) Mix_FadingChannel(this.mixChannel); 515 } 516 517 /++ 518 + Wraps `Mix_GetChunk` which gets the currently-playing `dsdl2.mixer.Chunk` in the channel 519 + 520 + This function is marked as `@system` due to the potential of referencing invalid memory. 521 + 522 + Returns: `dsdl2.mixer.Chunk` proxy to the playing chunk 523 + Throws: `dsdl2.SDLException` if failed to get the playing chunk 524 +/ 525 Chunk chunk() const @property @system { 526 if (Mix_Chunk* mixChunk = Mix_GetChunk(this.mixChannel)) { 527 return new Chunk(mixChunk, false); 528 } 529 else { 530 throw new SDLException; 531 } 532 } 533 534 /++ 535 + Wraps `Mix_PlayChannelTimed` which plays a `dsdl2.mixer.Chunk` in the channel 536 + 537 + Params: 538 + chunk = `dsdl2.mixer.Chunk` to be played 539 + loops = how many times the chunk should be played (`cast(uint) -1` for infinity) 540 + ms = number of milliseconds the chunk should be played for 541 + Throws: `dsdl2.SDLException` if failed to play the chunk 542 +/ 543 void play(const Chunk chunk, uint loops = 1, uint ms = cast(uint)-1) const @trusted { 544 if (Mix_PlayChannelTimed(this.mixChannel, cast(Mix_Chunk*) chunk.mixChunk, loops, ms) == -1) { 545 throw new SDLException; 546 } 547 } 548 549 /++ 550 + Wraps `Mix_FadeInChannelTimed` which plays a `dsdl2.mixer.Chunk` in the channel with a fade-in effect 551 + 552 + Params: 553 + chunk = `dsdl2.mixer.Chunk` to be played 554 + loops = how many times the chunk should be played (`cast(uint) -1` for infinity) 555 + fadeMs = number of milliseconds the chunk fades in before fully playing at full volume 556 + ms = number of milliseconds the chunk should be played for 557 + Throws: `dsdl2.SDLException` if failed to play the chunk 558 +/ 559 void fadeIn(const Chunk chunk, uint loops = 1, uint fadeMs = 0, uint ms = cast(uint)-1) const @trusted { 560 if (Mix_FadeInChannelTimed(this.mixChannel, cast(Mix_Chunk*) chunk.mixChunk, loops, fadeMs, ms) == -1) { 561 throw new SDLException; 562 } 563 } 564 565 /++ 566 + Wraps `Mix_HaltChannel` which halts the channel 567 + 568 + Throws: `dsdl2.SDLException` if failed to halt the channel 569 +/ 570 void halt() const @trusted { 571 if (Mix_HaltChannel(this.mixChannel) != 0) { 572 throw new SDLException; 573 } 574 } 575 576 /++ 577 + Acts as `Mix_HaltChannel(-1)` which halts all channels 578 + 579 + Throws: `dsdl2.SDLException` if failed to halt the channels 580 +/ 581 static void haltAll() @trusted { 582 if (Mix_HaltChannel(-1) != 0) { 583 throw new SDLException; 584 } 585 } 586 587 /++ 588 + Wraps `Mix_ExpireChannel` which halts the channel after a specified delay 589 + 590 + Params: 591 + ms = number of milliseconds before the channel halts 592 +/ 593 void expire(uint ms) const @trusted { 594 Mix_ExpireChannel(this.mixChannel, ms); 595 } 596 597 /++ 598 + Acts as `Mix_ExpireChannel(-1, ms)` which halts all channels after a specified delay 599 + 600 + Params: 601 + ms = number of milliseconds before the channels halt 602 +/ 603 static void expireAll(uint ms) @trusted { 604 Mix_ExpireChannel(-1, ms); 605 } 606 607 /++ 608 + Wraps `Mix_FadeOutChannel` which performs fade-out for whatever chunk is playing in the channel 609 + 610 + Params: 611 + fadeMs = number of milliseconds to fade-out before fully halting 612 +/ 613 void fadeOut(uint fadeMs) const @trusted { 614 Mix_FadeOutChannel(this.mixChannel, fadeMs); 615 } 616 617 /++ 618 + Acts as `Mix_FadeOutChannel(-1, fadeMs)` which performs fade-out for whatever chunks are playing in all channels 619 + 620 + Params: 621 + fadeMs = number of milliseconds to fade-out before fully halting 622 +/ 623 static void fadeOutAll(uint fadeMs) @trusted { 624 Mix_FadeOutChannel(-1, fadeMs); 625 } 626 627 /++ 628 + Wraps `Mix_Pause` which pauses the channel 629 +/ 630 void pause() const @trusted { 631 Mix_Pause(this.mixChannel); 632 } 633 634 /++ 635 + Acts as `Mix_Pause(-1)` which pauses all channels 636 +/ 637 static void pauseAll() @trusted { 638 Mix_Pause(-1); 639 } 640 641 /++ 642 + Wraps `Mix_Resume` which resumes the channel 643 +/ 644 void resume() const @trusted { 645 Mix_Resume(this.mixChannel); 646 } 647 648 /++ 649 + Acts as `Mix_Resume(-1)` which resumes all channels 650 +/ 651 static void resumeAll() @trusted { 652 Mix_Resume(-1); 653 } 654 } 655 656 /++ 657 + Gets `dsdl2.mixer.Channel` proxy instances of the available audio channels provided by SDL_mixer 658 + 659 + Returns: array of proxies to the available `dsdl2.mixer.Channel`s 660 + Throws: `dsdl2.SDLException` if failed to get the available audio channels 661 +/ 662 const(Channel[]) getChannels() @trusted { 663 int numChannels = Mix_AllocateChannels(-1); 664 665 static Channel[] channels; 666 if (channels !is null) { 667 size_t originalLength = channels.length; 668 channels.length = numChannels; 669 670 if (numChannels > originalLength) { 671 foreach (i; originalLength .. numChannels) { 672 channels[i] = new Channel(i.to!uint); 673 } 674 } 675 } 676 else { 677 channels = new Channel[](numChannels); 678 foreach (i; 0 .. numChannels) { 679 channels[i] = new Channel(i); 680 } 681 } 682 683 return channels; 684 } 685 686 /++ 687 + D class that wraps `Mix_Chunk` storing an audio chunk for playback 688 +/ 689 final class Chunk { 690 private bool isOwner = true; 691 private void* userRef = null; 692 693 @system Mix_Chunk* mixChunk = null; /// Internal `Mix_Chunk` pointer 694 695 /++ 696 + Constructs a `dsdl2.mixer.Chunk` from a vanilla `Mix_Chunk*` from bindbc-sdl 697 + 698 + Params: 699 + mixChunk = the `Mix_Chunk` pointer to manage 700 + isOwner = whether the instance owns the given `Mix_Chunk*` and should destroy it on its own 701 + userRef = optional pointer to maintain reference link, avoiding GC cleanup 702 +/ 703 this(Mix_Chunk* mixChunk, bool isOwner = true, void* userRef = null) @system 704 in { 705 assert(mixChunk !is null); 706 } 707 do { 708 this.mixChunk = mixChunk; 709 this.isOwner = isOwner; 710 this.userRef = userRef; 711 } 712 713 ~this() @trusted { 714 if (this.isOwner) { 715 Mix_FreeChunk(this.mixChunk); 716 } 717 } 718 719 @trusted invariant { // @suppress(dscanner.trust_too_much) 720 // Instance might be in an invalid state due to holding a non-owned externally-freed object when 721 // destructed in an unpredictable order. 722 if (!this.isOwner && GC.inFinalizer) { 723 return; 724 } 725 726 assert(this.mixChunk !is null); 727 } 728 729 /++ 730 + Equality operator overload 731 +/ 732 bool opEquals(const Chunk rhs) const @trusted { 733 return this.mixChunk is rhs.mixChunk; 734 } 735 736 /++ 737 + Gets the hash of the `dsdl2.mixer.Chunk` 738 + 739 + Returns: unique hash for the instance being the pointer of the internal `Mix_Chunk` pointer 740 +/ 741 override hash_t toHash() const @trusted { 742 return cast(hash_t) this.mixChunk; 743 } 744 745 /++ 746 + Formats the `dsdl2.mixer.Chunk` into its construction representation: 747 + `"dsdl2.mixer.Chunk(<mixChunk>)"` 748 + 749 + Returns: the formatted `string` 750 +/ 751 override string toString() const @trusted { 752 return "dsdl2.mixer.Chunk(0x%x)".format(this.mixChunk); 753 } 754 755 /++ 756 + Wraps `Mix_GetNumChunkDecoders` and `Mix_GetChunkDecoder` which return a list of chunk decoders 757 + 758 + Returns: names of the available chunk decoders 759 +/ 760 static string[] decoders() @property @trusted { 761 int numDecoders = Mix_GetNumChunkDecoders(); 762 if (numDecoders <= 0) { 763 throw new SDLException; 764 } 765 766 static string[] decoders; 767 if (decoders !is null) { 768 size_t originalLength = decoders.length; 769 decoders.length = numDecoders; 770 771 if (numDecoders > originalLength) { 772 foreach (i; originalLength .. numDecoders) { 773 decoders[i] = Mix_GetChunkDecoder(i.to!int).to!string; 774 } 775 } 776 } 777 else { 778 decoders = new string[](numDecoders); 779 foreach (i; 0 .. numDecoders) { 780 decoders[i] = Mix_GetChunkDecoder(i).to!string; 781 } 782 } 783 784 return decoders; 785 } 786 787 static if (sdlMixerSupport >= SDLMixerSupport.v2_0_2) { 788 /++ 789 + Wraps `Mix_HasChunkDecoder` (from SDL_mixer 2.0.2) which checks whether a chunk decoder is available 790 + 791 + Params: 792 + decoder = name of the chunk decoder 793 + Returns: `true` if available, otherwise `false` 794 +/ 795 static bool hasDecoder(string decoder) @trusted 796 in { 797 assert(dsdl2.mixer.getVersion() >= Version(2, 0, 2)); 798 } 799 do { 800 return Mix_HasChunkDecoder(decoder.toStringz()) == SDL_TRUE; 801 } 802 } 803 804 /++ 805 + Gets the raw PCM audio data buffer for the `dsdl2.mixer.Chunk` 806 + 807 + This function is marked as `@system` due to the potential of referencing invalid memory. 808 + 809 + Returns: slice of the buffer 810 +/ 811 inout(void[]) buffer() inout @property @trusted { 812 return (cast(inout(void*)) this.mixChunk.abuf)[0 .. this.mixChunk.alen]; 813 } 814 815 /++ 816 + Wraps `Mix_VolumeChunk` which gets the volume of the chunk 817 + 818 + Returns: volume ranging from `0` to `128` 819 +/ 820 ubyte volume() const @property @trusted { 821 return cast(ubyte) Mix_VolumeChunk(cast(Mix_Chunk*) this.mixChunk, -1); 822 } 823 824 /++ 825 + Wraps `Mix_VolumeChunk` which sets the volume of the chunk 826 + 827 + Params: 828 + newVolume = new volume ranging from `0` to `128` 829 +/ 830 void volume(ubyte newVolume) @property @trusted { 831 Mix_VolumeChunk(this.mixChunk, newVolume); 832 } 833 834 /++ 835 + Wraps `Mix_PlayChannelTimed` which plays the chunk on the first available free channel 836 + 837 + Params: 838 + loops = how many times the chunk should be played (`cast(uint) -1` for infinite) 839 + ms = number of milliseconds the chunk should be played for 840 + Returns: `dsdl2.mixer.Channel` the chunk is played on; `null` if no free channel 841 +/ 842 const(Channel) play(uint loops = 1, uint ms = cast(uint)-1) const @trusted { 843 int channel = Mix_PlayChannelTimed(-1, cast(Mix_Chunk*) this.mixChunk, loops, ms); 844 if (channel == -1) { 845 return null; 846 } 847 848 return getChannels()[channel]; 849 } 850 851 /++ 852 + Wraps `Mix_FadeInChannelTimed` which plays the chunk on the first available free channel with a fade-in effect 853 + 854 + Params: 855 + loops = how many times the chunk should be played (`cast(uint) -1` for infinite) 856 + fadeMs = number of milliseconds the chunk fades in before fully playing at full volume 857 + ms = number of milliseconds the chunk should be played for 858 + Returns: `dsdl2.mixer.Channel` the chunk is played on; `null` if no free channel 859 +/ 860 const(Channel) fadeIn(uint loops = 1, uint fadeMs = 0, uint ms = cast(uint)-1) const @trusted { 861 int channel = Mix_FadeInChannelTimed(-1, cast(Mix_Chunk*) this.mixChunk, loops, fadeMs, ms); 862 if (channel == -1) { 863 return null; 864 } 865 866 return getChannels()[channel]; 867 } 868 } 869 870 /++ 871 + Wraps `Mix_LoadWAV` which loads an audio file from the filesystem to a `dsdl2.mixer.Chunk` 872 + 873 + Params: 874 + file = path to the audio file 875 + Returns: loaded `dsdl2.mixer.Chunk` 876 + Throws: `dsdl2.SDLException` if unable to load 877 +/ 878 Chunk load(string file) @trusted { 879 Mix_Chunk* mixChunk = Mix_LoadWAV(file.toStringz()); 880 if (mixChunk !is null) { 881 return new Chunk(mixChunk); 882 } 883 else { 884 throw new SDLException; 885 } 886 } 887 888 /++ 889 + Wraps `Mix_LoadWAV_RW` which loads an audio file from a buffer to a `dsdl2.mixer.Chunk` 890 + 891 + Params: 892 + data = buffer of the audio file 893 + Returns: loaded `dsdl2.mixer.Chunk` 894 + Throws: `dsdl2.SDLException` if unable to load 895 +/ 896 Chunk loadRaw(const void[] data) @trusted { 897 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 898 if (sdlRWops is null) { 899 throw new SDLException; 900 } 901 902 Mix_Chunk* mixChunk = Mix_LoadWAV_RW(sdlRWops, 1); 903 if (mixChunk !is null) { 904 return new Chunk(mixChunk); 905 } 906 else { 907 throw new SDLException; 908 } 909 } 910 911 /++ 912 + Wraps `Mix_QuickLoad_RAW` which loads raw PCM audio data to a `dsdl2.mixer.Chunk` 913 + 914 + Params: 915 + pcm = buffer of the raw PCM audio data 916 + Returns: loaded `dsdl2.mixer.Chunk` 917 +/ 918 Chunk loadPCM(const void[] pcm) @trusted { 919 Mix_Chunk* mixChunk = Mix_QuickLoad_RAW(cast(ubyte*) pcm.ptr, pcm.length.to!int); 920 if (mixChunk !is null) { 921 return new Chunk(mixChunk); 922 } 923 else { 924 throw new SDLException; 925 } 926 } 927 928 /++ 929 + D class that wraps `Mix_Music` storing music for playback 930 +/ 931 final class Music { 932 private bool isOwner = true; 933 private void* userRef = null; 934 935 @system Mix_Music* mixMusic = null; /// Internal `Mix_Music` pointer 936 937 /++ 938 + Constructs a `dsdl2.mixer.Music` from a vanilla `Mix_Music*` from bindbc-sdl 939 + 940 + Params: 941 + mixMusic = the `Mix_Music` pointer to manage 942 + isOwner = whether the instance owns the given `Mix_Music*` and should destroy it on its own 943 + userRef = optional pointer to maintain reference link, avoiding GC cleanup 944 +/ 945 this(Mix_Music* mixMusic, bool isOwner = true, void* userRef = null) @system 946 in { 947 assert(mixMusic !is null); 948 } 949 do { 950 this.mixMusic = mixMusic; 951 this.isOwner = isOwner; 952 this.userRef = userRef; 953 } 954 955 ~this() @trusted { 956 if (this.isOwner) { 957 Mix_FreeMusic(this.mixMusic); 958 } 959 } 960 961 @trusted invariant { // @suppress(dscanner.trust_too_much) 962 // Instance might be in an invalid state due to holding a non-owned externally-freed object when 963 // destructed in an unpredictable order. 964 if (!this.isOwner && GC.inFinalizer) { 965 return; 966 } 967 968 assert(this.mixMusic !is null); 969 } 970 971 /++ 972 + Equality operator overload 973 +/ 974 bool opEquals(const Music rhs) const @trusted { 975 return this.mixMusic is rhs.mixMusic; 976 } 977 978 /++ 979 + Gets the hash of the `dsdl2.mixer.Music` 980 + 981 + Returns: unique hash for the instance being the pointer of the internal `Mix_Music` pointer 982 +/ 983 override hash_t toHash() const @trusted { 984 return cast(hash_t) this.mixMusic; 985 } 986 987 /++ 988 + Formats the `dsdl2.mixer.Music` into its construction representation: 989 + `"dsdl2.mixer.Music(<mixMusic>)"` 990 + 991 + Returns: the formatted `string` 992 +/ 993 override string toString() const @trusted { 994 return "dsdl2.mixer.Music(0x%x)".format(this.mixMusic); 995 } 996 997 /++ 998 + Wraps `Mix_GetNumMusicDecoders` and `Mix_GetMusicDecoder` which return a list of music decoders 999 + 1000 + Returns: names of the available music decoders 1001 +/ 1002 static string[] decoders() @property @trusted { 1003 int numDecoders = Mix_GetNumMusicDecoders(); 1004 if (numDecoders <= 0) { 1005 throw new SDLException; 1006 } 1007 1008 static string[] decoders; 1009 if (decoders !is null) { 1010 size_t originalLength = decoders.length; 1011 decoders.length = numDecoders; 1012 1013 if (numDecoders > originalLength) { 1014 foreach (i; originalLength .. numDecoders) { 1015 decoders[i] = Mix_GetMusicDecoder(i.to!int).to!string; 1016 } 1017 } 1018 } 1019 else { 1020 decoders = new string[](numDecoders); 1021 foreach (i; 0 .. numDecoders) { 1022 decoders[i] = Mix_GetMusicDecoder(i).to!string; 1023 } 1024 } 1025 1026 return decoders; 1027 } 1028 1029 static if (sdlMixerSupport >= SDLMixerSupport.v2_6) { 1030 /++ 1031 + Wraps `Mix_HasMusicDecoder` (from SDL_mixer 2.6) which checks whether a music decoder is available 1032 + 1033 + Params: 1034 + decoder = name of the music decoder 1035 + Returns: `true` if available, otherwise `false` 1036 +/ 1037 static bool hasDecoder(string decoder) @trusted 1038 in { 1039 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1040 } 1041 do { 1042 return Mix_HasMusicDecoder(decoder.toStringz()) == SDL_TRUE; 1043 } 1044 } 1045 1046 /++ 1047 + Wraps `Mix_GetMusicType` which gets the format type of the `dsdl2.mixer.Music` 1048 + 1049 + Returns: `dsdl2.mixer.MusicType` enumeration indicating the format type 1050 +/ 1051 MusicType type() const @property @trusted { 1052 return cast(MusicType) Mix_GetMusicType(this.mixMusic); 1053 } 1054 1055 static if (sdlMixerSupport >= SDLMixerSupport.v2_6) { 1056 /++ 1057 + Wraps `Mix_GetMusicTitle` (from SDL_mixer 2.6) which gets the title of the music 1058 + 1059 + Returns: title of the music 1060 +/ 1061 string title() const @property @trusted 1062 in { 1063 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1064 } 1065 do { 1066 return Mix_GetMusicTitle(this.mixMusic).to!string; 1067 } 1068 1069 /++ 1070 + Wraps `Mix_GetMusicTitleTag` (from SDL_mixer 2.6) which gets the title tag of the music 1071 + 1072 + Returns: title tag of the music 1073 +/ 1074 string titleTag() const @property @trusted 1075 in { 1076 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1077 } 1078 do { 1079 return Mix_GetMusicTitleTag(this.mixMusic).to!string; 1080 } 1081 1082 /++ 1083 + Wraps `Mix_GetMusicArtist` (from SDL_mixer 2.6) which gets the artist of the music 1084 + 1085 + Returns: artist of the music 1086 +/ 1087 string artistTag() const @property @trusted 1088 in { 1089 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1090 } 1091 do { 1092 return Mix_GetMusicArtistTag(this.mixMusic).to!string; 1093 } 1094 1095 /++ 1096 + Wraps `Mix_GetMusicAlbum` (from SDL_mixer 2.6) which gets the album of the music 1097 + 1098 + Returns: album of the music 1099 +/ 1100 string albumTag() const @property @trusted 1101 in { 1102 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1103 } 1104 do { 1105 return Mix_GetMusicAlbumTag(this.mixMusic).to!string; 1106 } 1107 1108 /++ 1109 + Wraps `Mix_GetMusicCopyright` (from SDL_mixer 2.6) which gets the copyright of the music 1110 + 1111 + Returns: copyright of the music 1112 +/ 1113 string copyrightTag() const @property @trusted 1114 in { 1115 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1116 } 1117 do { 1118 return Mix_GetMusicCopyrightTag(this.mixMusic).to!string; 1119 } 1120 1121 /++ 1122 + Wraps `Mix_GetMusicVolume` (from SDL_mixer 2.6) which gets the volume of the music 1123 + 1124 + Returns: volume ranging from `0` to `128` 1125 +/ 1126 ubyte volume() const @property @trusted 1127 in { 1128 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1129 } 1130 do { 1131 return cast(ubyte) Mix_GetMusicVolume(cast(Mix_Music*) this.mixMusic); 1132 } 1133 1134 /++ 1135 + Wraps `Mix_GetMusicPosition` (from SDL_mixer 2.6) which gets the timestamp of the music in seconds 1136 + 1137 + Returns: position timestamp of the music in seconds; `-1.0` if codec doesn't support 1138 +/ 1139 double position() const @property @trusted 1140 in { 1141 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1142 } 1143 do { 1144 return Mix_GetMusicPosition(cast(Mix_Music*) this.mixMusic); 1145 } 1146 1147 /++ 1148 + Wraps `Mix_MusicDuration` (from SDL_mixer 2.6) which gets the duration of the music in seconds 1149 + 1150 + Returns: duration of the music in seconds 1151 + Throws: `dsdl2.SDLException` if failed to get duration 1152 +/ 1153 double duration() const @property @trusted 1154 in { 1155 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1156 } 1157 do { 1158 double duration = Mix_MusicDuration(cast(Mix_Music*) this.mixMusic); 1159 if (duration == -1.0) { 1160 throw new SDLException; 1161 } 1162 1163 return duration; 1164 } 1165 1166 /++ 1167 + Wraps `Mix_GetMusicLoopStartTime` (from SDL_mixer 2.6) which gets the loop start time of the music 1168 + 1169 + Returns: loop start time of the music; `-1.0` if not used or codec doesn't support 1170 +/ 1171 double loopStartTime() const @property @trusted 1172 in { 1173 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1174 } 1175 do { 1176 return Mix_GetMusicLoopStartTime(cast(Mix_Music*) this.mixMusic); 1177 } 1178 1179 /++ 1180 + Wraps `Mix_GetMusicLoopEndTime` (from SDL_mixer 2.6) which gets the loop end time of the music 1181 + 1182 + Returns: loop end time of the music; `-1.0` if not used or codec doesn't support 1183 +/ 1184 double loopEndTime() const @property @trusted 1185 in { 1186 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1187 } 1188 do { 1189 return Mix_GetMusicLoopEndTime(cast(Mix_Music*) this.mixMusic); 1190 } 1191 1192 /++ 1193 + Wraps `Mix_GetMusicLoopLengthTime` (from SDL_mixer 2.6) which gets the loop length time of the music 1194 + 1195 + Returns: loop length time of the music; `-1.0` if not used or codec doesn't support 1196 +/ 1197 double loopLengthTime() const @property @trusted 1198 in { 1199 assert(dsdl2.mixer.getVersion() >= Version(2, 6)); 1200 } 1201 do { 1202 return Mix_GetMusicLoopLengthTime(cast(Mix_Music*) this.mixMusic); 1203 } 1204 } 1205 1206 /++ 1207 + Wraps `Mix_SetMusicPosition` which sets the timestamp position of the currently playing music 1208 + 1209 + Params: 1210 + newPosition = new timestamp position in seconds 1211 + Throws: `dsdl2.SDLException` if failed to set position 1212 +/ 1213 static void position(double newPosition) @property @trusted { 1214 if (Mix_SetMusicPosition(newPosition) != 0) { 1215 throw new SDLException; 1216 } 1217 } 1218 1219 /++ 1220 + Wraps `Mix_VolumeMusic` which gets the volume for music 1221 + 1222 + Returns: volume ranging from `0` to `128` 1223 +/ 1224 static ubyte volume() @property @trusted { 1225 return cast(ubyte) Mix_VolumeMusic(-1); 1226 } 1227 1228 /++ 1229 + Wraps `Mix_VolumeMusic` which sets the volume for music 1230 + 1231 + Params: 1232 + newVolume = new volume ranging from `0` to `128` 1233 +/ 1234 static void volume(ubyte newVolume) @property @trusted { 1235 Mix_VolumeMusic(newVolume); 1236 } 1237 1238 /++ 1239 + Wraps `Mix_PausedMusic` which checks whether music is paused 1240 + 1241 + Returns: `true` if music is paused, otherwise `false` 1242 +/ 1243 static bool paused() @trusted { 1244 return Mix_PausedMusic() == 1; 1245 } 1246 1247 /++ 1248 + Wraps `Mix_PlayingMusic` which checks whether music is playing 1249 + 1250 + Returns: `true` if music is playing, otherwise `false` 1251 +/ 1252 static bool playing() @trusted { 1253 return Mix_PlayingMusic() == 1; 1254 } 1255 1256 /++ 1257 + Wraps `Mix_FadingMusic` which gets the fading stage of the music 1258 + 1259 + Returns: `dsdl2.mixer.Fading` enumeration indicating the music's fading stage 1260 +/ 1261 static Fading fading() @property @trusted { 1262 return cast(Fading) Mix_FadingMusic(); 1263 } 1264 1265 /++ 1266 + Wraps `Mix_SetMusicCMD` which sets a command to be called when a new music is played 1267 + 1268 + Params: 1269 + newCommand = trigger command 1270 + Throws: `dsdl2.SDLException` if failed to set command 1271 +/ 1272 static void command(string newCommand) @property @trusted { 1273 if (Mix_SetMusicCMD(newCommand.toStringz()) != 0) { 1274 throw new SDLException; 1275 } 1276 } 1277 1278 /++ 1279 + Wraps `Mix_PlayMusic` which plays the `dsdl2.mixer.Music` 1280 + 1281 + Params: 1282 + loops = how many times the music should be played (`cast(uint) -1` for infinity) 1283 + Throws: `dsdl2.SDLException` if failed to play the music 1284 +/ 1285 void play(uint loops = 1) const @trusted { 1286 if (Mix_PlayMusic(cast(Mix_Music*) this.mixMusic, loops.to!int) != 0) { 1287 throw new SDLException; 1288 } 1289 } 1290 1291 /++ 1292 + Wraps `Mix_FadeInMusicPos` which plays the `dsdl2.mixer.Music` with a fade-in effect 1293 + 1294 + Params: 1295 + loops = how many times the chunk should be played (`cast(uint) -1` for infinity) 1296 + fadeMs = number of milliseconds the chunk fades in before fully playing at full volume 1297 + position = start timestamp of the music in seconds 1298 + Throws: `dsdl2.SDLException` if failed to play the music 1299 +/ 1300 void fadeIn(uint loops = 1, uint fadeMs = 0, double position = 0) const @trusted { 1301 if (Mix_FadeInMusicPos(cast(Mix_Music*) this.mixMusic, loops, fadeMs, position) != 0) { 1302 throw new SDLException; 1303 } 1304 } 1305 1306 /++ 1307 + Wraps `Mix_FadeOutMusic` which performs fade-out for the current music playing 1308 + 1309 + Params: 1310 + fadeMs = number of milliseconds to fade-out before fully halting 1311 +/ 1312 static void fadeOut(uint fadeMs) @trusted { 1313 Mix_FadeOutMusic(fadeMs.to!int); 1314 } 1315 1316 /++ 1317 + Wraps `Mix_HaltMusic` which halts the playing music 1318 +/ 1319 static void halt() @trusted { 1320 Mix_HaltMusic(); 1321 } 1322 1323 /++ 1324 + Wraps `Mix_PauseMusic` which pauses music playback 1325 +/ 1326 static void pause() @trusted { 1327 Mix_PauseMusic(); 1328 } 1329 1330 /++ 1331 + Wraps `Mix_ResumeMusic` which resumes music playback 1332 +/ 1333 static void resume() @trusted { 1334 Mix_ResumeMusic(); 1335 } 1336 1337 /++ 1338 + Wraps `Mix_RewindMusic` which rewinds music playback 1339 +/ 1340 static void rewind() @trusted { 1341 Mix_RewindMusic(); 1342 } 1343 } 1344 1345 /++ 1346 + Wraps `Mix_LoadMUS` which loads an audio file from the filesystem to a `dsdl2.mixer.Music` 1347 + 1348 + Params: 1349 + file = path to the audio file 1350 + Returns: loaded `dsdl2.mixer.Music` 1351 + Throws: `dsdl2.SDLException` if unable to load 1352 +/ 1353 Music loadMusic(string file) @trusted { 1354 Mix_Music* mixMusic = Mix_LoadMUS(file.toStringz()); 1355 if (mixMusic !is null) { 1356 return new Music(mixMusic); 1357 } 1358 else { 1359 throw new SDLException; 1360 } 1361 } 1362 1363 /++ 1364 + Wraps `Mix_LoadMUS_RW` which loads an audio file from a buffer to a `dsdl2.mixer.Music` 1365 + 1366 + Params: 1367 + data = buffer of the audio file 1368 + Returns: loaded `dsdl2.mixer.Music` 1369 + Throws: `dsdl2.SDLException` if unable to load 1370 +/ 1371 Music loadMusicRaw(const void[] data) @trusted { 1372 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 1373 if (sdlRWops is null) { 1374 throw new SDLException; 1375 } 1376 1377 Mix_Music* mixMusic = Mix_LoadMUS_RW(sdlRWops, 1); 1378 if (mixMusic !is null) { 1379 return new Music(mixMusic); 1380 } 1381 else { 1382 throw new SDLException; 1383 } 1384 } 1385 1386 /++ 1387 + Wraps `Mix_LoadMUSType_RW` which loads a typed audio file from a buffer to a `dsdl2.mixer.Music` 1388 + 1389 + Params: 1390 + data = buffer of the audio file 1391 + type = specified `dsdl2.mixer.MusicType` enumeration of the music 1392 + Returns: loaded `dsdl2.mixer.Music` 1393 + Throws: `dsdl2.SDLException` if unable to load 1394 +/ 1395 Music loadMusicRaw(const void[] data, MusicType type) @trusted { 1396 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 1397 if (sdlRWops is null) { 1398 throw new SDLException; 1399 } 1400 1401 Mix_Music* mixMusic = Mix_LoadMUSType_RW(sdlRWops, type, 1); 1402 if (mixMusic !is null) { 1403 return new Music(mixMusic); 1404 } 1405 else { 1406 throw new SDLException; 1407 } 1408 }