From 1c5be5d6aafdc7e2c89ee8d3e74f90d39d6ccc96 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 28 Mar 2026 00:28:30 +0100 Subject: [PATCH] Update SDL3_mixer to 3.2.0 --- README.md | 2 + units/SDL3_mixer.pas | 4808 ++++++++++++++++++++++++------------------ 2 files changed, 2784 insertions(+), 2026 deletions(-) diff --git a/README.md b/README.md index 88f303f..04d5757 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ The version number/tag (see [tags](https://github.com/PascalGameDevelopment/SDL3 ### v0.x (work in progress) +- updates SDL3_mixer to version 3.2.0 + ### v0.5 (15/08/2025) - adds SDL3_ttf unit diff --git a/units/SDL3_mixer.pas b/units/SDL3_mixer.pas index 361c3df..757263e 100644 --- a/units/SDL3_mixer.pas +++ b/units/SDL3_mixer.pas @@ -7,15 +7,101 @@ (https://github.com/PascalGameDevelopment/SDL3-for-Pascal) SPDX-License-Identifier: Zlib - Based on SDL3_mixer commit 4970bacc5954237037b00f8d2797e23b07782b2f } (** - * # CategorySDLMixer - * - * Header file for SDL_mixer library - * - * A simple library to play and mix sounds and musics + * # CategorySDLMixer + * + * SDL_mixer is a library to make complicated audio processing tasks easier. + * + * It offers audio file decoding, mixing multiple sounds together, basic 3D + * positional audio, and various audio effects. + * + * It can mix sound to multiple audio devices in real time, or generate mixed + * audio data to a memory buffer for any other use. It can do both at the same + * time! + * + * To use the library, first call MIX_Init(). Then create a mixer with + * MIX_CreateMixerDevice() (or MIX_CreateMixer() to render to memory). + * + * Once you have a mixer, you can load sound data with MIX_LoadAudio(), + * MIX_LoadAudio_IO(), or MIX_LoadAudioWithProperties(). Data gets loaded once + * and can be played over and over. + * + * When loading audio, SDL_mixer can parse out several metadata tag formats, + * such as ID3 and APE tags, and exposes this information through the + * MIX_GetAudioProperties() function. + * + * To play audio, you create a track with MIX_CreateTrack(). You need one + * track for each sound that will be played simultaneously; think of tracks as + * individual sliders on a mixer board. You might have loaded hundreds of + * audio files, but you probably only have a handful of tracks that you assign + * those loaded files to when they are ready to play, and reuse those tracks + * with different audio later. Tracks take their input from a MIX_Audio + * (static data to be played multiple times) or an SDL_AudioStream (streaming + * PCM audio the app supplies, possibly as needed). A third option is to + * supply an SDL_IOStream, to load and decode on the fly, which might be more + * efficient for background music that is only used once, etc. + * + * Assign input to a MIX_Track with MIX_SetTrackAudio(), + * MIX_SetTrackAudioStream(), or MIX_SetTrackIOStream(). + * + * Once a track has an input, start it playing with MIX_PlayTrack(). There are + * many options to this function to dictate mixing features: looping, fades, + * etc. + * + * Tracks can be tagged with arbitrary strings, like "music" or "ingame" or + * "ui". These tags can be used to start, stop, and pause a selection of + * tracks at the same moment. + * + * All significant portions of the mixing pipeline have callbacks, so that an + * app can hook in to the appropriate locations to examine or modify audio + * data as it passes through the mixer: a "raw" callback for raw PCM data + * decoded from an audio file without any modifications, a "cooked" callback + * for that same data after all transformations (fade, positioning, etc) have + * been processed, a "stopped" callback for when the track finishes mixing, a + * "postmix" callback for the final mixed audio about to be sent to the audio + * hardware to play. Additionally, you can use MIX_Group objects to mix a + * subset of playing tracks and access the data before it is mixed in with + * other tracks. All of this is optional, but allows for powerful access and + * control of the mixing process. + * + * SDL_mixer can also be used for decoding audio files without actually + * rendering a mix. This is done with MIX_AudioDecoder. Even though SDL_mixer + * handles decoding transparently when used as the audio engine for an app, + * and probably won't need this interface in that normal scenario, this can be + * useful when using a different audio library to access many file formats. + * + * This library offers several features on top of mixing sounds together: a + * track can have its own gain, to adjust its volume, in addition to a master + * gain applied as well. One can set the "frequency ratio" of a track or the + * final mixed output, to speed it up or slow it down, which also adjusts its + * pitch. A channel map can also be applied per-track, to change what speaker + * a given channel of audio is output to. + * + * Almost all timing in SDL_mixer is in _sample frames_. Stereo PCM audio data + * in Sint16 format takes 4 bytes per sample frame (2 bytes per sample times 2 + * channels), for example. This allows everything in SDL_mixer to run at + * sample-perfect accuracy, and it lets it run without concern for wall clock + * time--you can produce audio faster than real-time, if desired. The problem, + * though, is different pieces of audio at different _sample rates_ will + * produce a different number of sample frames for the same length of time. To + * deal with this, conversion routines are offered: MIX_TrackMSToFrames(), + * MIX_TrackFramesToMS(), etc. Functions that operate on multiple tracks at + * once will deal with time in milliseconds, so it can do these conversions + * internally; be sure to read the documentation for these small quirks! + * + * SDL_mixer offers basic positional audio: a simple 3D positioning API + * through MIX_SetTrack3DPosition() and MIX_SetTrackStereo(). The former will + * do simple distance attenuation and spatialization--on a stereo setup, you + * will hear sounds move from left to right--and on a surround-sound + * configuration, individual tracks can move around the user. The latter, + * MIX_SetTrackStereo(), will force a sound to the Front Left and Front Right + * speakers and let the app pan it left and right as desired. Either effect + * can be useful for different situations. SDL_mixer is not meant to be a full + * 3D audio engine, but rather Good Enough for many purposes; if something + * more powerful in terms of 3D audio is needed, consider a proper 3D library + * like OpenAL. *) {$DEFINE SDL_MIXER} @@ -59,2647 +145,3317 @@ interface {$I ctypes.inc} -const - SDL_MIXER_MAJOR_VERSION = 3; - SDL_MIXER_MINOR_VERSION = 0; - SDL_MIXER_MICRO_VERSION = 0; +{* + * An opaque object that represents a mixer. + * + * The MIX_Mixer is the toplevel object for this library. To use SDL_mixer, + * you must have at least one, but are allowed to have several. Each mixer is + * responsible for generating a single output stream of mixed audio, usually + * to an audio device for realtime playback. + * + * Mixers are either created to feed an audio device (through + * MIX_CreateMixerDevice()), or to generate audio to a buffer in memory, where + * it can be used for anything (through MIX_CreateMixer()). + * + * \since This datatype is available since SDL_mixer 3.0.0. + } +type + PPMIX_Mixer = ^PMIX_Mixer; + PMIX_Mixer = type Pointer; -Function SDL_MIXER_VERSION(): Integer; -Function SDL_MIXER_VERSION_ATLEAST(major, minor, micro: Integer): Boolean; +{* + * An opaque object that represents audio data. + * + * Generally you load audio data (in whatever file format) into SDL_mixer with + * MIX_LoadAudio() or one of its several variants, producing a MIX_Audio + * object. + * + * A MIX_Audio represents static audio data; it could be background music, or + * maybe a laser gun sound effect. It is loaded into RAM and can be played + * multiple times, possibly on different tracks at the same time. + * + * Unlike most other objects, MIX_Audio objects can be shared between mixers. + * + * \since This datatype is available since SDL_mixer 3.0.0. + } +type + PPMIX_Audio = ^PMIX_Audio; + PMIX_Audio = type Pointer; -{** - * This function gets the version of the dynamically linked SDL_mixer library. +{* + * An opaque object that represents a source of sound output to be mixed. * - * \returns SDL_mixer version. + * A MIX_Mixer has an arbitrary number of tracks, and each track manages its + * own unique audio to be mixed together. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_Version(): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Version' {$ENDIF} {$ENDIF}; + * Tracks also have other properties: gain, loop points, fading, 3D position, + * and other attributes that alter the produced sound; many can be altered + * during playback. + * + * \since This datatype is available since SDL_mixer 3.0.0. + } +type + PPMIX_Track = ^PMIX_Track; + PMIX_Track = type Pointer; -{** - * Initialization flags - *} +{* + * An opaque object that represents a grouping of tracks. + * + * SDL_mixer offers callbacks at various stages of the mixing pipeline to + * allow apps to view and manipulate data as it is transformed. Sometimes it + * is useful to hook in at a point where several tracks--but not all tracks-- + * have been mixed. For example, when a game is in some options menu, perhaps + * adjusting game audio but not UI sounds could be useful. + * + * SDL_mixer allows you to assign several tracks to a group, and receive a + * callback when that group has finished mixing, with a buffer of just that + * group's mixed audio, before it mixes into the final output. + * + * \since This datatype is available since SDL_mixer 3.0.0. + } type - TMIX_InitFlags = type cint; - PMIX_InitFlags = ^TMIX_InitFlags; - PPMIX_InitFlags = ^PMIX_InitFlags; + PPMIX_Group = ^PMIX_Group; + PMIX_Group = type Pointer; const - MIX_INIT_FLAC = TMIX_InitFlags($00000001); - MIX_INIT_MOD = TMIX_InitFlags($00000002); - MIX_INIT_MP3 = TMIX_InitFlags($00000008); - MIX_INIT_OGG = TMIX_InitFlags($00000010); - MIX_INIT_MID = TMIX_InitFlags($00000020); - MIX_INIT_OPUS = TMIX_InitFlags($00000040); - MIX_INIT_WAVPACK = TMIX_InitFlags($00000080); +{* + * The current major version of SDL_mixer headers. + * + * If this were SDL_mixer version 3.2.1, this value would be 3. + * + * \since This macro is available since SDL_mixer 3.0.0. + } + SDL_MIXER_MAJOR_VERSION = 3; +{* + * The current minor version of the SDL_mixer headers. + * + * If this were SDL_mixer version 3.2.1, this value would be 2. + * + * \since This macro is available since SDL_mixer 3.0.0. + } + SDL_MIXER_MINOR_VERSION = 2; +{* + * The current micro (or patchlevel) version of the SDL_mixer headers. + * + * If this were SDL_mixer version 3.2.1, this value would be 1. + * + * \since This macro is available since SDL_mixer 3.0.0. + } + SDL_MIXER_MICRO_VERSION = 0; -{** - * Initialize SDL_mixer. +{ +* This is the current version number macro of the SDL_mixer headers. +* +* \since This macro is available since SDL_mixer 3.0.0. +* +* \sa MIX_Version +} +function SDL_MIXER_VERSION(): Integer; + +{ +* This macro will evaluate to true if compiled with SDL_mixer at least X.Y.Z. +* +* \since This macro is available since SDL_mixer 3.0.0. +} +function SDL_MIXER_VERSION_ATLEAST(major, minor, micro: Integer): Boolean; + +{** +* Get the version of SDL_mixer that is linked against your program. +* +* If you are linking to SDL_mixer dynamically, then it is possible that the +* current version will be different than the version you compiled against. +* This function returns the current version, while SDL_MIXER_VERSION is the +* version you compiled with. +* +* This function may be called safely at any time, even before MIX_Init(). +* +* \returns the version of the linked library. +* +* \since This function is available since SDL_mixer 3.0.0. +* +* \sa SDL_MIXER_VERSION + *} +function Mix_Version(): cint; cdecl; + external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Version' {$ENDIF} {$ENDIF}; + +{* + * Initialize the SDL_mixer library. * - * This function loads dynamic libraries that SDL_mixer needs, and prepares - * them for use. + * This must be successfully called once before (almost) any other SDL_mixer + * function can be used. * - * Note that, unlike other SDL libraries, this call is optional! If you load a - * music file, SDL_mixer will handle initialization on the fly. This function - * will let you know, up front, whether a specific format will be available - * for use. + * It is safe to call this multiple times; the library will only initialize + * once, and won't deinitialize until MIX_Quit() has been called a matching + * number of times. Extra attempts to init report success. * - * Flags should be one or more flags from MIX_InitFlags OR'd together. It - * returns the flags successfully initialized, or 0 on failure. + * \returns true on success, false on error; call SDL_GetError() for details. * - * Currently, these flags are: + * \threadsafety This function is not thread safe. * - * - `MIX_INIT_FLAC` - * - `MIX_INIT_MOD` - * - `MIX_INIT_MP3` - * - `MIX_INIT_OGG` - * - `MIX_INIT_MID` - * - `MIX_INIT_OPUS` - * - `MIX_INIT_WAVPACK` + * \since This function is available since SDL_mixer 3.0.0. * - * More flags may be added in a future SDL_mixer release. + * \sa MIX_Quit + } +function MIX_Init: Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_Init' {$ENDIF} {$ENDIF}; + +{* + * Deinitialize the SDL_mixer library. * - * This function may need to load external shared libraries to support various - * codecs, which means this function can fail to initialize that support on an - * otherwise-reasonable system if the library isn't available; this is not - * just a question of exceptional circumstances like running out of memory at - * startup! + * This must be called when done with the library, probably at the end of your + * program. * - * Note that you may call this function more than once to initialize with - * additional flags. The return value will reflect both new flags that - * successfully initialized, and also include flags that had previously been - * initialized as well. + * It is safe to call this multiple times; the library will only deinitialize + * once, when this function is called the same number of times as MIX_Init was + * successfully called. * - * As this will return previously-initialized flags, it's legal to call this - * with zero (no flags set). This is a safe no-op that can be used to query - * the current initialization state without changing it at all. + * Once you have successfully deinitialized the library, it is safe to call + * MIX_Init to reinitialize it for further use. * - * Since this returns previously-initialized flags as well as new ones, and - * you can call this with zero, you should not check for a zero return value - * to determine an error condition. Instead, you should check to make sure all - * the flags you require are set in the return value. If you have a game with - * data in a specific format, this might be a fatal error. If you're a generic - * media player, perhaps you are fine with only having WAV and MP3 support and - * can live without Opus playback, even if you request support for everything. + * On successful deinitialization, SDL_mixer will destroy almost all created + * objects, including objects of type: * - * Unlike other SDL satellite libraries, calls to Mix_Init do not stack; a - * single call to Mix_Quit() will deinitialize everything and does not have to - * be paired with a matching Mix_Init call. For that reason, it's considered - * best practices to have a single Mix_Init and Mix_Quit call in your program. - * While this isn't required, be aware of the risks of deviating from that - * behavior. + * - MIX_Mixer + * - MIX_Track + * - MIX_Audio + * - MIX_Group + * - MIX_AudioDecoder * - * After initializing SDL_mixer, the next step is to open an audio device to - * prepare to play sound (with Mix_OpenAudio()), and load audio data to play - * with that device. + * ...which is to say: it's possible a single call to this function will clean + * up anything it allocated, stop all audio output, close audio devices, etc. + * Don't attempt to destroy objects after this call. The app is still + * encouraged to manage their resources carefully and clean up first, treating + * this function as a safety net against memory leaks. * - * \param flags initialization flags, OR'd together. - * \returns all currently initialized flags. + * \threadsafety This function is not thread safe. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_Quit - *} -function Mix_Init(flags: TMix_InitFlags): TMIX_InitFlags; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Init' {$ENDIF} {$ENDIF}; + * \sa MIX_Init + } +procedure MIX_Quit; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_Quit' {$ENDIF} {$ENDIF}; -{** - * Deinitialize SDL_mixer. +{* + * Report the number of audio decoders available for use. + * + * An audio decoder is what turns specific audio file formats into usable PCM + * data. For example, there might be an MP3 decoder, or a WAV decoder, etc. + * SDL_mixer probably has several decoders built in. * - * This should be the last function you call in SDL_mixer, after freeing all - * other resources and closing all audio devices. This will unload any shared - * libraries it is using for various codecs. + * The return value can be used to call MIX_GetAudioDecoder() in a loop. * - * After this call, a call to Mix_Init(0) will return 0 (no codecs loaded). + * The number of decoders available is decided during MIX_Init() and does not + * change until the library is deinitialized. * - * You can safely call Mix_Init() to reload various codec support after this - * call. + * \returns the number of decoders available. * - * Unlike other SDL satellite libraries, calls to Mix_Init do not stack; a - * single call to Mix_Quit() will deinitialize everything and does not have to - * be paired with a matching Mix_Init call. For that reason, it's considered - * best practices to have a single Mix_Init and Mix_Quit call in your program. - * While this isn't required, be aware of the risks of deviating from that - * behavior. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_Init - *} -procedure Mix_Quit(); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Quit' {$ENDIF} {$ENDIF}; + * \sa MIX_GetAudioDecoder + } +function MIX_GetNumAudioDecoders: cint; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetNumAudioDecoders' {$ENDIF} {$ENDIF}; -{** - * The default mixer has 8 simultaneous mixing channels - *} -const - MIX_CHANNELS = 8; - -{* Good default values for a PC soundcard *} -const - MIX_DEFAULT_FREQUENCY = 44100; - MIX_DEFAULT_FORMAT = SDL_AUDIO_S16; - MIX_DEFAULT_CHANNELS = 2; - MIX_MAX_VOLUME = 128; // Volume of a chunk +{* + * Report the name of a specific audio decoders. + * + * An audio decoder is what turns specific audio file formats into usable PCM + * data. For example, there might be an MP3 decoder, or a WAV decoder, etc. + * SDL_mixer probably has several decoders built in. + * + * The names are capital English letters and numbers, low-ASCII. They don't + * necessarily map to a specific file format; Some decoders, like "XMP" + * operate on multiple file types, and more than one decoder might handle the + * same file type, like "DRMP3" vs "MPG123". Note that in that last example, + * neither decoder is called "MP3". + * + * The index of a specific decoder is decided during MIX_Init() and does not + * change until the library is deinitialized. Valid indices are between zero + * and the return value of MIX_GetNumAudioDecoders(). + * + * The returned Pointer is const memory owned by SDL_mixer; do not free it. + * + * \param index the index of the decoder to query. + * \returns a UTF-8 (really, ASCII) string of the decoder's name, or nil if + * `index` is invalid. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_GetNumAudioDecoders + } +function MIX_GetAudioDecoder(index: cint): PAnsiChar; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetAudioDecoder' {$ENDIF} {$ENDIF}; -{** - * The internal format for an audio chunk - *} -type - TMix_Chunk = record - allocated: cint; - abuf: pcuint8; - alen: cuint32; - volume: cuint8; - end; - PMix_Chunk = ^TMix_Chunk; - PPMix_Chunk = ^PMix_Chunk; +{* + * Create a mixer that plays sound directly to an audio device. + * + * This is usually the function you want, vs MIX_CreateMixer(). + * + * You can choose a specific device ID to open, following SDL's usual rules, + * but often the correct choice is to specify + * SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK and let SDL figure out what device to use + * (and seamlessly transition you to new hardware if the default changes). + * + * Only playback devices make sense here. Attempting to open a recording + * device will fail. + * + * This will call SDL_Init(SDL_INIT_AUDIO) internally; it's safe to call + * SDL_Init() before this call, too, if you intend to enumerate audio devices + * to choose one to open here. + * + * An audio format can be requested, and the system will try to set the + * hardware to those specifications, or as close as possible, but this is just + * a hint. SDL_mixer will handle all data conversion behind the scenes in any + * case, and specifying a nil spec is a reasonable choice. The best reason to + * specify a format is because you know all your data is in that format and it + * might save some unnecessary CPU time on conversion. + * + * The actual device format chosen is available through MIX_GetMixerFormat(). + * + * Once a mixer is created, next steps are usually to load audio (through + * MIX_LoadAudio() and friends), create a track (MIX_CreateTrack()), and play + * that audio through that track. + * + * When done with the mixer, it can be destroyed with MIX_DestroyMixer(). + * + * \param devid the device to open for playback, or + * SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK for the default. + * \param spec the audio format to request from the device. May be nil. + * \returns a mixer that can be used to play audio, or nil on failure; call + * SDL_GetError() for more information. + * + * \threadsafety This function should only be called on the main thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_CreateMixer + * \sa MIX_DestroyMixer + } +function MIX_CreateMixerDevice(devid: TSDL_AudioDeviceID; spec: PSDL_AudioSpec): PMIX_Mixer; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_CreateMixerDevice' {$ENDIF} {$ENDIF}; -{** - * The different fading types supported - *} -type - PPMix_Fading = ^TMix_Fading; - PMix_Fading = ^TMix_Fading; - TMix_Fading = type cint; +{* + * Create a mixer that generates audio to a memory buffer. + * + * Usually you want MIX_CreateMixerDevice() instead of this function. The + * mixer created here can be used with MIX_Generate() to produce more data on + * demand, as fast as desired. + * + * An audio format must be specified. This is the format it will output in. + * This cannot be nil. + * + * Once a mixer is created, next steps are usually to load audio (through + * MIX_LoadAudio() and friends), create a track (MIX_CreateTrack()), and play + * that audio through that track. + * + * When done with the mixer, it can be destroyed with MIX_DestroyMixer(). + * + * \param spec the audio format that mixer will generate. + * \returns a mixer that can be used to generate audio, or nil on failure; + * call SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_CreateMixerDevice + * \sa MIX_DestroyMixer + } +function MIX_CreateMixer(spec: PSDL_AudioSpec): PMIX_Mixer; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_CreateMixer' {$ENDIF} {$ENDIF}; -const - MIX_NO_FADING = TMix_Fading(0); - MIX_FADING_OUT = TMix_Fading(1); - MIX_FADING_IN = TMix_Fading(2); +{* + * Free a mixer. + * + * If this mixer was created with MIX_CreateMixerDevice(), this function will + * also close the audio device and call SDL_QuitSubSystem(SDL_INIT_AUDIO). + * + * Any MIX_Group or MIX_Track created for this mixer will also be destroyed. + * Do not access them again or attempt to destroy them after the device is + * destroyed. MIX_Audio objects will not be destroyed, since they can be + * shared between mixers (but those will all be destroyed during MIX_Quit()). + * + * \param mixer the mixer to destroy. + * + * \threadsafety If this is used with a MIX_Mixer from MIX_CreateMixerDevice, + * then this function should only be called on the main thread. + * If this is used with a MIX_Mixer from MIX_CreateMixer, then + * it is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_CreateMixerDevice + * \sa MIX_CreateMixer + } +procedure MIX_DestroyMixer(mixer: PMIX_Mixer); cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_DestroyMixer' {$ENDIF} {$ENDIF}; -{** - * These are types of music files (not libraries used to load them) - *} -type - PPMix_MusicType = ^PMix_MusicType; - PMix_MusicType = ^TMix_MusicType; - TMix_MusicType = type cint; +{* + * Get the properties associated with a mixer. + * + * The following read-only properties are provided by SDL_mixer: + * + * - `MIX_PROP_MIXER_DEVICE_NUMBER`: the SDL_AudioDeviceID that this mixer has + * opened for playback. This will be zero (no device) if the mixer was + * created with Mix_CreateMixer() instead of Mix_CreateMixerDevice(). + * + * \param mixer the mixer to query. + * \returns a valid property ID on success or 0 on failure; call + * SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_GetMixerProperties(mixer: PMIX_Mixer): TSDL_PropertiesID; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetMixerProperties' {$ENDIF} {$ENDIF}; const - MUS_NONE = TMix_MusicType(0); - MUS_WAV = TMix_MusicType(1); - MUS_MOD = TMix_MusicType(2); - MUS_MID = TMix_MusicType(3); - MUS_OGG = TMix_MusicType(4); - MUS_MP3 = TMix_MusicType(5); - MUS_FLAC = TMix_MusicType(6); - MUS_OPUS = TMix_MusicType(7); - MUS_WAVPACK = TMix_MusicType(8); - MUS_GME = TMix_MusicType(9); - -{** - * The internal format for a music chunk interpreted via codecs - *} -type - PMix_Music = type Pointer; - PPMix_Music = ^PMix_Music; + MIX_PROP_MIXER_DEVICE_NUMBER = 'SDL_mixer.mixer.device'; -{** - * Open an audio device for playback. - * - * An audio device is what generates sound, so the app must open one to make - * noise. - * - * This function will check if SDL's audio system is initialized, and if not, - * it will initialize it by calling `SDL_Init(SDL_INIT_AUDIO)` on your behalf. - * You are free to (and encouraged to!) initialize it yourself before calling - * this function, as this gives your program more control over the process. - * - * If you aren't particularly concerned with the specifics of the audio - * device, and your data isn't in a specific format, you can pass a NULL for - * the `spec` parameter and SDL_mixer will choose a reasonable default. - * SDL_mixer will convert audio data you feed it to the hardware's format - * behind the scenes. - * - * That being said, if you have control of your audio data and you know its - * format ahead of time, you may save CPU time by opening the audio device in - * that exact format so SDL_mixer does not have to spend time converting - * anything behind the scenes, and can just pass the data straight through to - * the hardware. - * - * The other reason to care about specific formats: if you plan to touch the - * mix buffer directly (with Mix_SetPostMix, a registered effect, or - * Mix_HookMusic), you might have code that expects it to be in a specific - * format, and you should specify that here. - * - * This function allows you to select specific audio hardware on the system - * with the `devid` parameter. If you specify 0, SDL_mixer will choose the - * best default it can on your behalf (which, in many cases, is exactly what - * you want anyhow). This is equivalent to specifying - * `SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK`, but is less wordy. SDL_mixer does not - * offer a mechanism to determine device IDs to open, but you can use - * SDL_GetAudioOutputDevices() to get a list of available devices. If you do - * this, be sure to call `SDL_Init(SDL_INIT_AUDIO)` first to initialize SDL's - * audio system! - * - * If this function reports success, you are ready to start making noise! Load - * some audio data and start playing! - * - * When done with an audio device, probably at the end of the program, the app - * should close the audio with Mix_CloseAudio(). - * - * \param devid the device name to open, or 0 for a reasonable default. - * \param spec the audio format you'd like SDL_mixer to work in. +{* + * Get the audio format a mixer is generating. + * + * Generally you don't need this information, as SDL_mixer will convert data + * as necessary between inputs you provide and its output format, but it might + * be useful if trying to match your inputs to reduce conversion and + * resampling costs. + * + * For mixers created with MIX_CreateMixerDevice(), this is the format of the + * audio device (and may change later if the device itself changes; SDL_mixer + * will seamlessly handle this change internally, though). + * + * For mixers created with MIX_CreateMixer(), this is the format that + * MIX_Generate() will produce, as requested at create time, and does not + * change. + * + * Note that internally, SDL_mixer will work in SDL_AUDIO_F32 format before + * outputting the format specified here, so it would be more efficient to + * match input data to that, not the final output format. + * + * \param mixer the mixer to query. + * \param spec where to store the mixer audio format. * \returns true on success or false on failure; call SDL_GetError() for more * information. * - * \since This function is available since SDL_mixer 3.0.0. + * \threadsafety It is safe to call this function from any thread. * - * \sa Mix_CloseAudio - * \sa Mix_QuerySpec - *} -function Mix_OpenAudio(devid: TSDL_AudioDeviceID; const spec: PSDL_AudioSpec): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_OpenAudio' {$ENDIF} {$ENDIF}; + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_GetMixerFormat(mixer: PMIX_Mixer; spec: PSDL_AudioSpec): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetMixerFormat' {$ENDIF} {$ENDIF}; -{** - * Suspend or resume the whole audio output. +{* + * Lock a mixer by obtaining its internal mutex. + * + * While locked, the mixer will not be able to mix more audio or change its + * internal state in another thread. Those other threads will block until the + * mixer is unlocked again. + * + * Under the hood, this function calls SDL_LockMutex(), so all the same rules + * apply: the lock can be recursive, it must be unlocked the same number of + * times from the same thread that locked it, etc. + * + * Just about every SDL_mixer API _also_ locks the mixer while doing its work, + * as does the SDL audio device thread while actual mixing is in progress, so + * basic use of this library never requires the app to explicitly lock the + * device to be thread safe. There are two scenarios where this can be useful, + * however: + * + * - The app has a provided a callback that the mixing thread might call, and + * there is some app state that needs to be protected against race + * conditions as changes are made and mixing progresses simultaneously. Any + * lock can be used for this, but this is a conveniently-available lock. + * - The app wants to make multiple, atomic changes to the mix. For example, + * to start several tracks at the exact same moment, one would lock the + * mixer, call MIX_PlayTrack multiple times, and then unlock again; all the + * tracks will start mixing on the same sample frame. * - * \param pause_on 1 to pause audio output, or 0 to resume. + * Each call to this function must be paired with a call to MIX_UnlockMixer + * from the same thread. It is safe to lock a mixer multiple times; it remains + * locked until the final matching unlock call. + * + * Do not lock the mixer for significant amounts of time, or it can cause + * audio dropouts. Just do simply things quickly and unlock again. + * + * Locking a nil mixer is a safe no-op. + * + * \param mixer the mixer to lock. May be nil. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_PauseAudio(pause_on: cint); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_PauseAudio' {$ENDIF} {$ENDIF}; + * + * \sa MIX_UnlockMixer + } +procedure MIX_LockMixer(mixer: PMIX_Mixer); cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_LockMixer' {$ENDIF} {$ENDIF}; -{** - * Find out what the actual audio device parameters are. +{* + * Unlock a mixer previously locked by a call to MIX_LockMixer(). + * + * While locked, the mixer will not be able to mix more audio or change its + * internal state another thread. Those other threads will block until the + * mixer is unlocked again. * - * Note this is only important if the app intends to touch the audio buffers - * being sent to the hardware directly. If an app just wants to play audio - * files and let SDL_mixer handle the low-level details, this function can - * probably be ignored. + * Under the hood, this function calls SDL_LockMutex(), so all the same rules + * apply: the lock can be recursive, it must be unlocked the same number of + * times from the same thread that locked it, etc. * - * If the audio device is not opened, this function will return 0. + * Unlocking a nil mixer is a safe no-op. * - * \param frequency On return, will be filled with the audio device's - * frequency in Hz. - * \param format On return, will be filled with the audio device's format. - * \param channels On return, will be filled with the audio device's channel - * count. - * \returns true if the audio device has been opened, false otherwise. + * \param mixer the mixer to unlock. May be nil. + * + * \threadsafety This call must be paired with a previous MIX_LockMixer call + * on the same thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_OpenAudio - *} -function Mix_QuerySpec(frequency: pcint; format: PSDL_AudioFormat; channels: pcint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_QuerySpec' {$ENDIF} {$ENDIF}; + * \sa MIX_LockMixer + } +procedure MIX_UnlockMixer(mixer: PMIX_Mixer); cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_UnlockMixer' {$ENDIF} {$ENDIF}; -{** - * Dynamically change the number of channels managed by the mixer. +{* + * Load audio for playback from an SDL_IOStream. * - * SDL_mixer deals with "channels," which is not the same thing as the - * mono/stereo channels; they might be better described as "tracks," as each - * one corresponds to a separate source of audio data. Three different WAV - * files playing at the same time would be three separate SDL_mixer channels, - * for example. + * In normal usage, apps should load audio once, maybe at startup, then play + * it multiple times. * - * An app needs as many channels as it has audio data it wants to play - * simultaneously, mixing them into a single stream to send to the audio - * device. + * When loading audio, it will be cached fully in RAM in its original data + * format. Each time it plays, the data will be decoded. For example, an MP3 + * will be stored in memory in MP3 format and be decompressed on the fly + * during playback. This is a tradeoff between i/o overhead and memory usage. * - * SDL_mixer allocates `MIX_CHANNELS` (currently 8) channels when you open an - * audio device, which may be more than an app needs, but if the app needs - * more or wants less, this function can change it. + * If `predecode` is true, the data will be decompressed during load and + * stored as raw PCM data. This might dramatically increase loading time and + * memory usage, but there will be no need to decompress data during playback. * - * If decreasing the number of channels, any upper channels currently playing - * are stopped. This will deregister all effects on those channels and call - * any callback specified by Mix_ChannelFinished() for each removed channel. + * (One could also use MIX_SetTrackIOStream() to bypass loading the data into + * RAM upfront at all, but this offers still different tradeoffs. The correct + * approach depends on the app's needs and employing different approaches in + * different situations can make sense.) * - * If `numchans` is less than zero, this will return the current number of - * channels without changing anything. + * MIX_Audio objects can be shared between mixers. This function takes a + * MIX_Mixer, to imply this is the most likely place it will be used and + * loading should try to match its audio format, but the resulting audio can + * be used elsewhere. If `mixer` is nil, SDL_mixer will set reasonable + * defaults. * - * \param numchans the new number of channels, or < 0 to query current channel - * count. - * \returns the new number of allocated channels. + * Once a MIX_Audio is created, it can be assigned to a MIX_Track with + * MIX_SetTrackAudio(), or played without any management with MIX_PlayAudio(). * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_AllocateChannels(numchans: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_AllocateChannels' {$ENDIF} {$ENDIF}; - -{** - * Load a supported audio format into a chunk. - * - * SDL_mixer has two separate data structures for audio data. One it calls a - * "chunk," which is meant to be a file completely decoded into memory up - * front, and the other it calls "music" which is a file intended to be - * decoded on demand. Originally, simple formats like uncompressed WAV files - * were meant to be chunks and compressed things, like MP3s, were meant to be - * music, and you would stream one thing for a game's music and make repeating - * sound effects with the chunks. - * - * In modern times, this isn't split by format anymore, and most are - * interchangeable, so the question is what the app thinks is worth - * predecoding or not. Chunks might take more memory, but once they are loaded - * won't need to decode again, whereas music always needs to be decoded on the - * fly. Also, crucially, there are as many channels for chunks as the app can - * allocate, but SDL_mixer only offers a single "music" channel. - * - * If `closeio` is true, the IOStream will be closed before returning, whether - * this function succeeds or not. SDL_mixer reads everything it needs from the - * IOStream during this call in any case. - * - * There is a separate function (a macro, before SDL_mixer 3.0.0) to read - * files from disk without having to deal with SDL_IOStream: - * `Mix_LoadWAV("filename.wav")` will call this function and manage those - * details for you. - * - * When done with a chunk, the app should dispose of it with a call to - * Mix_FreeChunk(). - * - * \param src an SDL_IOStream that data will be read from. - * \param closeio true to close the SDL_IOStream before returning, false to - * leave it open. - * \returns a new chunk, or NULL on error. - * - * \since This function is available since SDL_mixer 3.0.0 - * - * \sa Mix_LoadWAV - * \sa Mix_FreeChunk - *} -function Mix_LoadWAV_IO(src: PSDL_IOStream; closeio: Boolean): PMix_Chunk; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_LoadWAV_IO' {$ENDIF} {$ENDIF}; - -{** - * Load a supported audio format into a chunk. - * - * SDL_mixer has two separate data structures for audio data. One it calls a - * "chunk," which is meant to be a file completely decoded into memory up - * front, and the other it calls "music" which is a file intended to be - * decoded on demand. Originally, simple formats like uncompressed WAV files - * were meant to be chunks and compressed things, like MP3s, were meant to be - * music, and you would stream one thing for a game's music and make repeating - * sound effects with the chunks. - * - * In modern times, this isn't split by format anymore, and most are - * interchangeable, so the question is what the app thinks is worth - * predecoding or not. Chunks might take more memory, but once they are loaded - * won't need to decode again, whereas music always needs to be decoded on the - * fly. Also, crucially, there are as many channels for chunks as the app can - * allocate, but SDL_mixer only offers a single "music" channel. - * - * If you would rather use the abstract SDL_IOStream interface to load data - * from somewhere other than the filesystem, you can use Mix_LoadWAV_IO() - * instead. - * - * When done with a chunk, the app should dispose of it with a call to - * Mix_FreeChunk(). - * - * Note that before SDL_mixer 3.0.0, this function was a macro that called - * Mix_LoadWAV_IO(), creating a IOStream and setting `closeio` to true. This - * macro has since been promoted to a proper API function. Older binaries - * linked against a newer SDL_mixer will still call Mix_LoadWAV_IO directly, - * as they are using the macro, which was available since the dawn of time. - * - * \param file the filesystem path to load data from. - * \returns a new chunk, or NULL on error. - * - * \since This function is available since SDL_mixer 3.0.0 - * - * \sa Mix_LoadWAV_IO - * \sa Mix_FreeChunk - *} -function Mix_LoadWAV(const file_: PAnsiChar): PMix_Chunk; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_LoadWAV' {$ENDIF} {$ENDIF}; - -{** - * Load a supported audio format into a music object. + * When done with a MIX_Audio, it can be freed with MIX_DestroyAudio(). * - * SDL_mixer has two separate data structures for audio data. One it calls a - * "chunk," which is meant to be a file completely decoded into memory up - * front, and the other it calls "music" which is a file intended to be - * decoded on demand. Originally, simple formats like uncompressed WAV files - * were meant to be chunks and compressed things, like MP3s, were meant to be - * music, and you would stream one thing for a game's music and make repeating - * sound effects with the chunks. + * This function loads data from an SDL_IOStream. There is also a version that + * loads from a path on the filesystem (MIX_LoadAudio()), and one that accepts + * properties for ultimate control (MIX_LoadAudioWithProperties()). * - * In modern times, this isn't split by format anymore, and most are - * interchangeable, so the question is what the app thinks is worth - * predecoding or not. Chunks might take more memory, but once they are loaded - * won't need to decode again, whereas music always needs to be decoded on the - * fly. Also, crucially, there are as many channels for chunks as the app can - * allocate, but SDL_mixer only offers a single "music" channel. + * The SDL_IOStream provided must be able to seek, or loading will fail. If + * the stream can't seek (data is coming from an HTTP connection, etc), + * consider caching the data to memory or disk first and creating a new stream + * to read from there. * - * When done with this music, the app should dispose of it with a call to - * Mix_FreeMusic(). + * \param mixer a mixer this audio is intended to be used with. May be nil. + * \param io the SDL_IOStream to load data from. + * \param predecode if true, data will be fully uncompressed before returning. + * \param closeio true if SDL_mixer should close `io` before returning + * (success or failure). + * \returns an audio object that can be used to make sound on a mixer, or nil + * on failure; call SDL_GetError() for more information. * - * \param file a file path from where to load music data. - * \returns a new music object, or NULL on error. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_FreeMusic - *} -function Mix_LoadMUS(const file_: PAnsiChar): PMix_Music; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_LoadMUS' {$ENDIF} {$ENDIF}; + * \sa MIX_DestroyAudio + * \sa MIX_SetTrackAudio + * \sa MIX_LoadAudio + * \sa MIX_LoadAudioWithProperties + } +function MIX_LoadAudio_IO(mixer: PMIX_Mixer; io: PSDL_IOStream; predecode: Boolean; closeio: Boolean): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_LoadAudio_IO' {$ENDIF} {$ENDIF}; -{** - * Load a supported audio format into a music object. +{* + * Load audio for playback from a file. * - * SDL_mixer has two separate data structures for audio data. One it calls a - * "chunk," which is meant to be a file completely decoded into memory up - * front, and the other it calls "music" which is a file intended to be - * decoded on demand. Originally, simple formats like uncompressed WAV files - * were meant to be chunks and compressed things, like MP3s, were meant to be - * music, and you would stream one thing for a game's music and make repeating - * sound effects with the chunks. + * This is equivalent to calling: * - * In modern times, this isn't split by format anymore, and most are - * interchangeable, so the question is what the app thinks is worth - * predecoding or not. Chunks might take more memory, but once they are loaded - * won't need to decode again, whereas music always needs to be decoded on the - * fly. Also, crucially, there are as many channels for chunks as the app can - * allocate, but SDL_mixer only offers a single "music" channel. + * ```c + * MIX_LoadAudio_IO(mixer, SDL_IOFromFile(path, "rb"), predecode, true); + * ``` * - * If `closeio` is true, the IOStream will be closed before returning, whether - * this function succeeds or not. SDL_mixer reads everything it needs from the - * IOStream during this call in any case. + * This function loads data from a path on the filesystem. There is also a + * version that loads from an SDL_IOStream (MIX_LoadAudio_IO()), and one that + * accepts properties for ultimate control (MIX_LoadAudioWithProperties()). + * + * \param mixer a mixer this audio is intended to be used with. May be nil. + * \param path the path on the filesystem to load data from. + * \param predecode if true, data will be fully uncompressed before returning. + * \returns an audio object that can be used to make sound on a mixer, or nil + * on failure; call SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_DestroyAudio + * \sa MIX_SetTrackAudio + * \sa MIX_LoadAudio_IO + * \sa MIX_LoadAudioWithProperties + } +function MIX_LoadAudio(mixer: PMIX_Mixer; path: PAnsiChar; predecode: Boolean): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_LoadAudio' {$ENDIF} {$ENDIF}; + +{* + * Load audio for playback from a memory buffer without making a copy. * - * As a convenience, there is a function to read files from disk without - * having to deal with SDL_IOStream: `Mix_LoadMUS("filename.mp3")` will manage - * those details for you. + * When loading audio through most other LoadAudio functions, the data will be + * cached fully in RAM in its original data format, for decoding on-demand. + * This function does most of the same work as those functions, but instead + * uses a buffer of memory provided by the app that it does not make a copy + * of. + * + * This buffer must live for the entire time the returned MIX_Audio lives, as + * the mixer will access the buffer whenever it needs to mix more data. + * + * This function is meant to maximize efficiency: if the data is already in + * memory and can remain there, don't copy it. This data can be in any + * supported audio file format (WAV, MP3, etc); it will be decoded on the fly + * while mixing. Unlike MIX_LoadAudio(), there is no `predecode` option + * offered here, as this is meant to optimize for data that's already in + * memory and intends to exist there for significant time; since predecoding + * would only need the file format data once, upfront, one could simply wrap + * it in SDL_CreateIOFromConstMem() and pass that to MIX_LoadAudio_IO(). + * + * MIX_Audio objects can be shared between multiple mixers. The `mixer` + * parameter just suggests the most likely mixer to use this audio, in case + * some optimization might be applied, but this is not required, and a nil + * mixer may be specified. + * + * If `free_when_done` is true, SDL_mixer will call `SDL_free(data)` when the + * returned MIX_Audio is eventually destroyed. This can be useful when the + * data is not static, but rather loaded elsewhere for this specific MIX_Audio + * and simply wants to avoid the extra copy. + * + * As audio format information is obtained from the file format metadata, this + * isn't useful for raw PCM data; in that case, use MIX_LoadRawAudioNoCopy() + * instead, which offers an SDL_AudioSpec. + * + * Once a MIX_Audio is created, it can be assigned to a MIX_Track with + * MIX_SetTrackAudio(), or played without any management with MIX_PlayAudio(). + * + * When done with a MIX_Audio, it can be freed with MIX_DestroyAudio(). + * + * \param mixer a mixer this audio is intended to be used with. May be nil. + * \param data the buffer where the audio data lives. + * \param datalen the size, in bytes, of the buffer. + * \param free_when_done if true, `data` will be given to SDL_free() when the + * MIX_Audio is destroyed. + * \returns an audio object that can be used to make sound on a mixer, or nil + * on failure; call SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_DestroyAudio + * \sa MIX_SetTrackAudio + * \sa MIX_LoadRawAudioNoCopy + * \sa MIX_LoadAudio_IO + } +function MIX_LoadAudioNoCopy(mixer: PMIX_Mixer; data: Pointer; datalen: csize_t; free_when_done: Boolean): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_LoadAudioNoCopy' {$ENDIF} {$ENDIF}; + +{* + * Load audio for playback through a collection of properties. + * + * Please see MIX_LoadAudio_IO() for a description of what the various + * LoadAudio functions do. This function uses properties to dictate how it + * operates, and exposes functionality the other functions don't provide. + * + * SDL_PropertiesID are discussed in + * [SDL's documentation](https://wiki.libsdl.org/SDL3/CategoryProperties) + * . + * + * These are the supported properties: + * + * - `MIX_PROP_AUDIO_LOAD_IOSTREAM_POINTER`: a Pointer to an SDL_IOStream to + * be used to load audio data. Required. This stream must be able to seek! + * - `MIX_PROP_AUDIO_LOAD_CLOSEIO_BOOLEAN`: true if SDL_mixer should close the + * SDL_IOStream before returning (success or failure). + * - `MIX_PROP_AUDIO_LOAD_PREDECODE_BOOLEAN`: true if SDL_mixer should fully + * decode and decompress the data before returning. Otherwise it will be + * stored in its original state and decompressed on demand. + * - `MIX_PROP_AUDIO_LOAD_PREFERRED_MIXER_POINTER`: a Pointer to a MIX_Mixer, + * in case steps can be made to match its format when decoding. Optional. + * - `MIX_PROP_AUDIO_LOAD_SKIP_METADATA_TAGS_BOOLEAN`: true to skip parsing + * metadata tags, like ID3 and APE tags. This can be used to speed up + * loading _if the data definitely doesn't have these tags_. Some decoders + * will fail if these tags are present when this property is true. + * - `MIX_PROP_AUDIO_DECODER_STRING`: the name of the decoder to use for this + * data. Optional. If not specified, SDL_mixer will examine the data and + * choose the best decoder. These names are the same returned from + * MIX_GetAudioDecoder(). + * + * Specific decoders might accept additional custom properties, such as where + * to find soundfonts for MIDI playback, etc. + * + * \param props a set of properties on how to load audio. + * \returns an audio object that can be used to make sound on a mixer, or nil + * on failure; call SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_DestroyAudio + * \sa MIX_SetTrackAudio + * \sa MIX_LoadAudio + * \sa MIX_LoadAudio_IO + } +function MIX_LoadAudioWithProperties(props: TSDL_PropertiesID): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_LoadAudioWithProperties' {$ENDIF} {$ENDIF}; + + const + MIX_PROP_AUDIO_LOAD_IOSTREAM_POINTER = 'SDL_mixer.audio.load.iostream'; + MIX_PROP_AUDIO_LOAD_CLOSEIO_BOOLEAN = 'SDL_mixer.audio.load.closeio'; + MIX_PROP_AUDIO_LOAD_PREDECODE_BOOLEAN = 'SDL_mixer.audio.load.predecode'; + MIX_PROP_AUDIO_LOAD_PREFERRED_MIXER_POINTER = 'SDL_mixer.audio.load.preferred_mixer'; + MIX_PROP_AUDIO_LOAD_SKIP_METADATA_TAGS_BOOLEAN = 'SDL_mixer.audio.load.skip_metadata_tags'; + MIX_PROP_AUDIO_DECODER_STRING = 'SDL_mixer.audio.decoder'; + +{* + * Load raw PCM data from an SDL_IOStream. + * + * There are other options for _streaming_ raw PCM: an SDL_AudioStream can be + * connected to a track, as can an SDL_IOStream, and will read from those + * sources on-demand when it is time to mix the audio. This function is useful + * for loading static audio data that is meant to be played multiple times. + * + * This function will load the raw data in its entirety and cache it in RAM. + * + * MIX_Audio objects can be shared between multiple mixers. The `mixer` + * parameter just suggests the most likely mixer to use this audio, in case + * some optimization might be applied, but this is not required, and a nil + * mixer may be specified. + * + * \param mixer a mixer this audio is intended to be used with. May be nil. + * \param io the SDL_IOStream to load data from. + * \param spec what format the raw data is in. + * \param closeio true if SDL_mixer should close `io` before returning + * (success or failure). + * \returns an audio object that can be used to make sound on a mixer, or nil + * on failure; call SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_DestroyAudio + * \sa MIX_SetTrackAudio + * \sa MIX_LoadRawAudio + * \sa MIX_LoadRawAudioNoCopy + * \sa MIX_LoadAudio_IO + } +function MIX_LoadRawAudio_IO(mixer: PMIX_Mixer; io: PSDL_IOStream; spec: PSDL_AudioSpec; closeio: Boolean): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_LoadRawAudio_IO' {$ENDIF} {$ENDIF}; + +{* + * Load raw PCM data from a memory buffer. + * + * There are other options for _streaming_ raw PCM: an SDL_AudioStream can be + * connected to a track, as can an SDL_IOStream, and will read from those + * sources on-demand when it is time to mix the audio. This function is useful + * for loading static audio data that is meant to be played multiple times. + * + * This function will load the raw data in its entirety and cache it in RAM, + * allocating a copy. If the original data will outlive the created MIX_Audio, + * you can use MIX_LoadRawAudioNoCopy() to avoid extra allocations and copies. + * + * MIX_Audio objects can be shared between multiple mixers. The `mixer` + * parameter just suggests the most likely mixer to use this audio, in case + * some optimization might be applied, but this is not required, and a nil + * mixer may be specified. + * + * \param mixer a mixer this audio is intended to be used with. May be nil. + * \param data the raw PCM data to load. + * \param datalen the size, in bytes, of the raw PCM data. + * \param spec what format the raw data is in. + * \returns an audio object that can be used to make sound on a mixer, or nil + * on failure; call SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_DestroyAudio + * \sa MIX_SetTrackAudio + * \sa MIX_LoadRawAudio_IO + * \sa MIX_LoadRawAudioNoCopy + * \sa MIX_LoadAudio_IO + } +function MIX_LoadRawAudio(mixer: PMIX_Mixer; data: Pointer; datalen: csize_t; spec: PSDL_AudioSpec): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_LoadRawAudio' {$ENDIF} {$ENDIF}; + +{* + * Load raw PCM data from a memory buffer without making a copy. + * + * This buffer must live for the entire time the returned MIX_Audio lives, as + * the mixer will access the buffer whenever it needs to mix more data. + * + * This function is meant to maximize efficiency: if the data is already in + * memory and can remain there, don't copy it. But it can also lead to some + * interesting tricks, like changing the buffer's contents to alter multiple + * playing tracks at once. (But, of course, be careful when being too clever.) + * + * MIX_Audio objects can be shared between multiple mixers. The `mixer` + * parameter just suggests the most likely mixer to use this audio, in case + * some optimization might be applied, but this is not required, and a nil + * mixer may be specified. + * + * If `free_when_done` is true, SDL_mixer will call `SDL_free(data)` when the + * returned MIX_Audio is eventually destroyed. This can be useful when the + * data is not static, but rather composed dynamically for this specific + * MIX_Audio and simply wants to avoid the extra copy. + * + * \param mixer a mixer this audio is intended to be used with. May be nil. + * \param data the buffer where the raw PCM data lives. + * \param datalen the size, in bytes, of the buffer. + * \param spec what format the raw data is in. + * \param free_when_done if true, `data` will be given to SDL_free() when the + * MIX_Audio is destroyed. + * \returns an audio object that can be used to make sound on a mixer, or nil + * on failure; call SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_DestroyAudio + * \sa MIX_SetTrackAudio + * \sa MIX_LoadRawAudio + * \sa MIX_LoadRawAudio_IO + * \sa MIX_LoadAudio_IO + } +function MIX_LoadRawAudioNoCopy(mixer: PMIX_Mixer; data: Pointer; datalen: csize_t; spec: PSDL_AudioSpec; free_when_done: Boolean): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_LoadRawAudioNoCopy' {$ENDIF} {$ENDIF}; + +{* + * Create a MIX_Audio that generates a sinewave. + * + * This is useful just to have _something_ to play, perhaps for testing or + * debugging purposes. * - * This function attempts to guess the file format from incoming data. If the - * caller knows the format, or wants to force it, it should use - * Mix_LoadMUSType_IO() instead. + * You specify its frequency in Hz (determines the pitch of the sinewave's + * audio) and amplitude (determines the volume of the sinewave: 1.0f is very + * loud, 0.0f is silent). + * + * A number of milliseconds of audio to generate can be specified. Specifying + * a value less than zero will generate infinite audio (when assigned to a + * MIX_Track, the sinewave will play forever). + * + * MIX_Audio objects can be shared between multiple mixers. The `mixer` + * parameter just suggests the most likely mixer to use this audio, in case + * some optimization might be applied, but this is not required, and a nil + * mixer may be specified. + * + * \param mixer a mixer this audio is intended to be used with. May be nil. + * \param hz the sinewave's frequency in Hz. + * \param amplitude the sinewave's amplitude from 0.0f to 1.0f. + * \param ms the maximum number of milliseconds of audio to generate, or less + * than zero to generate infinite audio. + * \returns an audio object that can be used to make sound on a mixer, or nil + * on failure; call SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_DestroyAudio + * \sa MIX_SetTrackAudio + * \sa MIX_LoadAudio_IO + } +function MIX_CreateSineWaveAudio(mixer: PMIX_Mixer; hz: cint; amplitude: cfloat; ms: cint64): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_CreateSineWaveAudio' {$ENDIF} {$ENDIF}; + +{* + * Get the properties associated with a MIX_Audio. * - * When done with this music, the app should dispose of it with a call to - * Mix_FreeMusic(). + * SDL_mixer offers some properties of its own, but this can also be a + * convenient place to store app-specific data. * - * \param src an SDL_IOStream that data will be read from. - * \param closeio true to close the SDL_IOStream before returning, false to - * leave it open. - * \returns a new music object, or NULL on error. + * A SDL_PropertiesID is created the first time this function is called for a + * given MIX_Audio, if necessary. + * + * The following read-only properties are provided by SDL_mixer: + * + * - `MIX_PROP_METADATA_TITLE_STRING`: the audio's title ("Smells Like Teen + * Spirit"). + * - `MIX_PROP_METADATA_ARTIST_STRING`: the audio's artist name ("Nirvana"). + * - `MIX_PROP_METADATA_ALBUM_STRING`: the audio's album name ("Nevermind"). + * - `MIX_PROP_METADATA_COPYRIGHT_STRING`: the audio's copyright info + * ("Copyright (c) 1991") + * - `MIX_PROP_METADATA_TRACK_NUMBER`: the audio's track number on the album + * (1) + * - `MIX_PROP_METADATA_TOTAL_TRACKS_NUMBER`: the total tracks on the album + * (13) + * - `MIX_PROP_METADATA_YEAR_NUMBER`: the year the audio was released (1991) + * - `MIX_PROP_METADATA_DURATION_FRAMES_NUMBER`: The sample frames worth of + * PCM data that comprise this audio. It might be off by a little if the + * decoder only knows the duration as a unit of time. + * - `MIX_PROP_METADATA_DURATION_INFINITE_BOOLEAN`: if true, audio never runs + * out of sound to generate. This isn't necessarily always known to + * SDL_mixer, though. + * + * Other properties, documented with MIX_LoadAudioWithProperties(), may also + * be present. + * + * Note that the metadata properties are whatever SDL_mixer finds in things + * like ID3 tags, and they often have very little standardized formatting, may + * be missing, and can be completely wrong if the original data is + * untrustworthy (like an MP3 from a P2P file sharing service). + * + * \param audio the audio to query. + * \returns a valid property ID on success or 0 on failure; call + * SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - * - * \sa Mix_FreeMusic - *} -function Mix_LoadMUS_IO(src: PSDL_IOStream; closeio: Boolean): PMix_Music; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_LoadMUS_IO' {$ENDIF} {$ENDIF}; + } +function MIX_GetAudioProperties(audio: PMIX_Audio): TSDL_PropertiesID; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetAudioProperties' {$ENDIF} {$ENDIF}; -{** - * Load an audio format into a music object, assuming a specific format. - * - * SDL_mixer has two separate data structures for audio data. One it calls a - * "chunk," which is meant to be a file completely decoded into memory up - * front, and the other it calls "music" which is a file intended to be - * decoded on demand. Originally, simple formats like uncompressed WAV files - * were meant to be chunks and compressed things, like MP3s, were meant to be - * music, and you would stream one thing for a game's music and make repeating - * sound effects with the chunks. - * - * In modern times, this isn't split by format anymore, and most are - * interchangeable, so the question is what the app thinks is worth - * predecoding or not. Chunks might take more memory, but once they are loaded - * won't need to decode again, whereas music always needs to be decoded on the - * fly. Also, crucially, there are as many channels for chunks as the app can - * allocate, but SDL_mixer only offers a single "music" channel. - * - * This function loads music data, and lets the application specify the type - * of music being loaded, which might be useful if SDL_mixer cannot figure it - * out from the data stream itself. - * - * Currently, the following types are supported: - * - * - `MUS_NONE` (SDL_mixer should guess, based on the data) - * - `MUS_WAV` (Microsoft WAV files) - * - `MUS_MOD` (Various tracker formats) - * - `MUS_MID` (MIDI files) - * - `MUS_OGG` (Ogg Vorbis files) - * - `MUS_MP3` (MP3 files) - * - `MUS_FLAC` (FLAC files) - * - `MUS_OPUS` (Opus files) - * - `MUS_WAVPACK` (WavPack files) - * - * If `closeio` is true, the IOStream will be closed before returning, whether - * this function succeeds or not. SDL_mixer reads everything it needs from the - * IOStream during this call in any case. - * - * As a convenience, there is a function to read files from disk without - * having to deal with SDL_IOStream: `Mix_LoadMUS("filename.mp3")` will manage - * those details for you (but not let you specify the music type explicitly).. - * - * When done with this music, the app should dispose of it with a call to - * Mix_FreeMusic(). - * - * \param src an SDL_IOStream that data will be read from. - * \param mtype the type of audio data provided by `src`. - * \param closeio true to close the SDL_IOStream before returning, false to - * leave it open. - * \returns a new music object, or NULL on error. - * - * \since This function is available since SDL_mixer 3.0.0. - * - * \sa Mix_FreeMusic - *} -function Mix_LoadMUSType_IO(src: PSDL_IOStream; mtype: TMix_MusicType; closeio: Boolean): PMix_Music; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_LoadMUSType_IO' {$ENDIF} {$ENDIF}; +const + MIX_PROP_METADATA_TITLE_STRING = 'SDL_mixer.metadata.title'; + MIX_PROP_METADATA_ARTIST_STRING = 'SDL_mixer.metadata.artist'; + MIX_PROP_METADATA_ALBUM_STRING = 'SDL_mixer.metadata.album'; + MIX_PROP_METADATA_COPYRIGHT_STRING = 'SDL_mixer.metadata.copyright'; + MIX_PROP_METADATA_TRACK_NUMBER = 'SDL_mixer.metadata.track'; + MIX_PROP_METADATA_TOTAL_TRACKS_NUMBER = 'SDL_mixer.metadata.total_tracks'; + MIX_PROP_METADATA_YEAR_NUMBER = 'SDL_mixer.metadata.year'; + MIX_PROP_METADATA_DURATION_FRAMES_NUMBER = 'SDL_mixer.metadata.duration_frames'; + MIX_PROP_METADATA_DURATION_INFINITE_BOOLEAN = 'SDL_mixer.metadata.duration_infinite'; -{** - * Load a WAV file from memory as quickly as possible. +{* + * Get the length of a MIX_Audio's playback in sample frames. * - * Unlike Mix_LoadWAV_IO, this function has several requirements, and unless - * you control all your audio data and know what you're doing, you should - * consider this function unsafe and not use it. + * This information is also available via the + * MIX_PROP_METADATA_DURATION_FRAMES_NUMBER property, but it's common enough + * to provide a simple accessor function. * - * - The provided audio data MUST be in Microsoft WAV format. - * - The provided audio data shouldn't use any strange WAV extensions. - * - The audio data MUST be in the exact same format as the audio device. This - * function will not attempt to convert it, or even verify it's in the right - * format. - * - The audio data must be valid; this function does not know the size of the - * memory buffer, so if the WAV data is corrupted, it can read past the end - * of the buffer, causing a crash. - * - The audio data must live at least as long as the returned Mix_Chunk, - * because SDL_mixer will use that data directly and not make a copy of it. + * This reports the length of the data in _sample frames_, so sample-perfect + * mixing can be possible. Sample frames are only meaningful as a measure of + * time if the sample rate (frequency) is also known. To convert from sample + * frames to milliseconds, use MIX_AudioFramesToMS(). * - * This function will do NO error checking! Be extremely careful here! + * Not all audio file formats can report the complete length of the data they + * will produce through decoding: some can't calculate it, some might produce + * infinite audio. * - * (Seriously, use Mix_LoadWAV_IO instead.) + * Also, some file formats can only report duration as a unit of time, which + * means SDL_mixer might have to estimate sample frames from that information. + * With less precision, the reported duration might be off by a few sample + * frames in either direction. * - * If this function is successful, the provided memory buffer must remain - * available until Mix_FreeChunk() is called on the returned chunk. + * This will return a value >= 0 if a duration is known. It might also return + * MIX_DURATION_UNKNOWN or MIX_DURATION_INFINITE. * - * \param mem memory buffer containing of a WAV file. - * \returns a new chunk, or NULL on error. + * \param audio the audio to query. + * \returns the length of the audio in sample frames, or MIX_DURATION_UNKNOWN + * or MIX_DURATION_INFINITE. * - * \since This function is available since SDL_mixer 3.0.0. + * \threadsafety It is safe to call this function from any thread. * - * \sa Mix_LoadWAV_IO - * \sa Mix_FreeChunk - *} -function Mix_QuickLoad_WAV(mem: pcuint8): PMix_Chunk; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_QuickLoad_WAV' {$ENDIF} {$ENDIF}; + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_GetAudioDuration(audio: PMIX_Audio): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetAudioDuration' {$ENDIF} {$ENDIF}; -{** - * Load a raw audio data from memory as quickly as possible. +const + MIX_DURATION_UNKNOWN = -1; + MIX_DURATION_INFINITE = -2; + + {* + * Query the initial audio format of a MIX_Audio. * - * The audio data MUST be in the exact same format as the audio device. This - * function will not attempt to convert it, or even verify it's in the right - * format. + * Note that some audio files can change format in the middle; some explicitly + * support this, but a more common example is two MP3 files concatenated + * together. In many cases, SDL_mixer will correctly handle these sort of + * files, but this function will only report the initial format a file uses. * - * If this function is successful, the provided memory buffer must remain - * available until Mix_FreeChunk() is called on the returned chunk. + * \param audio the audio to query. + * \param spec on success, audio format details will be stored here. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \param mem memory buffer containing raw PCM data. - * \param len length of buffer pointed to by `mem`, in bytes. - * \returns a new chunk, or NULL on error. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - * - * \sa Mix_FreeChunk - *} -function Mix_QuickLoad_RAW(mem: pcuint8; len: cuint32): PMix_Chunk; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_QuickLoad_RAW' {$ENDIF} {$ENDIF}; + } +function MIX_GetAudioFormat(audio: PMIX_Audio; spec: PSDL_AudioSpec): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetAudioFormat' {$ENDIF} {$ENDIF}; -{** - * Free an audio chunk. +{* + * Destroy the specified audio. * - * An app should call this function when it is done with a Mix_Chunk and wants - * to dispose of its resources. + * MIX_Audio is reference-counted internally, so this function only unrefs it. + * If doing so causes the reference count to drop to zero, the MIX_Audio will + * be deallocated. This allows the system to safely operate if the audio is + * still assigned to a MIX_Track at the time of destruction. The actual + * destroying will happen when the track stops using it. * - * SDL_mixer will stop any channels this chunk is currently playing on. This - * will deregister all effects on those channels and call any callback - * specified by Mix_ChannelFinished() for each removed channel. + * But from the caller's perspective, once this function is called, it should + * assume the `audio` Pointer has become invalid. * - * \param chunk the chunk to free. + * Destroying a nil MIX_Audio is a legal no-op. * - * \since This function is available since SDL_mixer 3.0.0. + * \param audio the audio to destroy. * - * \sa Mix_LoadWAV - * \sa Mix_LoadWAV_IO - * \sa Mix_QuickLoad_WAV - * \sa Mix_QuickLoad_RAW - *} -procedure Mix_FreeChunk(chunk: PMix_Chunk); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FreeChunk' {$ENDIF} {$ENDIF}; + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + } +procedure MIX_DestroyAudio(audio: PMIX_Audio); cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_DestroyAudio' {$ENDIF} {$ENDIF}; -{** - * Free a music object. +{* + * Create a new track on a mixer. * - * If this music is currently playing, it will be stopped. + * A track provides a single source of audio. All currently-playing tracks + * will be processed and mixed together to form the final output from the + * mixer. * - * If this music is in the process of fading out (via Mix_FadeOutMusic()), - * this function will *block* until the fade completes. If you need to avoid - * this, be sure to call Mix_HaltMusic() before freeing the music. + * There are no limits to the number of tracks one may create, beyond running + * out of memory, but in normal practice there are a small number of tracks + * that are reused between all loaded audio as appropriate. * - * \param music the music object to free. + * Tracks are unique to a specific MIX_Mixer and can't be transferred between + * them. + * + * \param mixer the mixer on which to create this track. + * \returns a new MIX_Track on success, nil on error; call SDL_GetError() for + * more informations. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_LoadMUS - * \sa Mix_LoadMUS_IO - * \sa Mix_LoadMUSType_IO - *} -procedure Mix_FreeMusic(music: PMix_Music); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FreeMusic' {$ENDIF} {$ENDIF}; + * \sa MIX_DestroyTrack + } +function MIX_CreateTrack(mixer: PMIX_Mixer): PMIX_Track; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_CreateTrack' {$ENDIF} {$ENDIF}; -{** - * Get a list of chunk decoders that this build of SDL_mixer provides. +{* + * Destroy the specified track. * - * This list can change between builds AND runs of the program, if external - * libraries that add functionality become available. You must successfully - * call Mix_OpenAudio() before calling this function, as decoders are - * activated at device open time. + * If the track is currently playing, it will be stopped immediately, without + * any fadeout. If there is a callback set through + * MIX_SetTrackStoppedCallback(), it will _not_ be called. * - * Appearing in this list doesn't promise your specific audio file will - * decode...but it's handy to know if you have, say, a functioning Ogg Vorbis - * install. + * If the mixer is currently mixing in another thread, this will block until + * it finishes. * - * These return values are static, read-only data; do not modify or free it. - * The pointers remain valid until you call Mix_CloseAudio(). + * Destroying a nil MIX_Track is a legal no-op. * - * \returns number of chunk decoders available. + * \param track the track to destroy. * - * \since This function is available since SDL_mixer 3.0.0. + * \threadsafety It is safe to call this function from any thread. * - * \sa Mix_GetChunkDecoder - * \sa Mix_HasChunkDecoder - *} -function Mix_GetNumChunkDecoders(): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetNumChunkDecoders' {$ENDIF} {$ENDIF}; + * \since This function is available since SDL_mixer 3.0.0. + } +procedure MIX_DestroyTrack(track: PMIX_Track); cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_DestroyTrack' {$ENDIF} {$ENDIF}; -{** - * Get a chunk decoder's name. +{* + * Get the properties associated with a track. * - * The requested decoder's index must be between zero and - * Mix_GetNumChunkDecoders()-1. It's safe to call this with an invalid index; - * this function will return NULL in that case. + * Currently SDL_mixer assigns no properties of its own to a track, but this + * can be a convenient place to store app-specific data. * - * This list can change between builds AND runs of the program, if external - * libraries that add functionality become available. You must successfully - * call Mix_OpenAudio() before calling this function, as decoders are - * activated at device open time. + * A SDL_PropertiesID is created the first time this function is called for a + * given track. * - * \param index index of the chunk decoder. - * \returns the chunk decoder's name. + * \param track the track to query. + * \returns a valid property ID on success or 0 on failure; call + * SDL_GetError() for more information. * - * \since This function is available since SDL_mixer 3.0.0. + * \threadsafety It is safe to call this function from any thread. * - * \sa Mix_GetNumChunkDecoders - *} -function Mix_GetChunkDecoder(index: cint): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetChunkDecoder' {$ENDIF} {$ENDIF}; + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_GetTrackProperties(track: PMIX_Track): TSDL_PropertiesID; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackProperties' {$ENDIF} {$ENDIF}; -{** - * Check if a chunk decoder is available by name. +{* + * Get the MIX_Mixer that owns a MIX_Track. * - * This result can change between builds AND runs of the program, if external - * libraries that add functionality become available. You must successfully - * call Mix_OpenAudio() before calling this function, as decoders are - * activated at device open time. + * This is the mixer Pointer that was passed to MIX_CreateTrack(). * - * Decoder names are arbitrary but also obvious, so you have to know what - * you're looking for ahead of time, but usually it's the file extension in - * capital letters (some example names are "AIFF", "VOC", "WAV"). + * \param track the track to query. + * \returns the mixer associated with the track, or nil on error; call + * SDL_GetError() for more information. * - * \param name the decoder name to query. - * \returns true if a decoder by that name is available, false otherwise. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - * - * \sa Mix_GetNumChunkDecoders - * \sa Mix_GetChunkDecoder - *} -function Mix_HasChunkDecoder(const name: PAnsiChar): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_HasChunkDecoder' {$ENDIF} {$ENDIF}; + } +function MIX_GetTrackMixer(track: PMIX_Track): PMIX_Mixer; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackMixer' {$ENDIF} {$ENDIF}; -{** - * Get a list of music decoders that this build of SDL_mixer provides. +{* + * Set a MIX_Track's input to a MIX_Audio. * - * This list can change between builds AND runs of the program, if external - * libraries that add functionality become available. You must successfully - * call Mix_OpenAudio() before calling this function, as decoders are - * activated at device open time. + * A MIX_Audio is audio data stored in RAM (possibly still in a compressed + * form). One MIX_Audio can be assigned to multiple tracks at once. * - * Appearing in this list doesn't promise your specific audio file will - * decode...but it's handy to know if you have, say, a functioning Ogg Vorbis - * install. + * Once a track has a valid input, it can start mixing sound by calling + * MIX_PlayTrack(), or possibly MIX_PlayTag(). * - * These return values are static, read-only data; do not modify or free it. - * The pointers remain valid until you call Mix_CloseAudio(). + * Calling this function with a nil audio input is legal, and removes any + * input from the track. If the track was currently playing, the next time the + * mixer runs, it'll notice this and mark the track as stopped, calling any + * assigned MIX_TrackStoppedCallback. * - * \returns number of music decoders available. + * It is legal to change the input of a track while it's playing, however some + * states, like loop points, may cease to make sense with the new audio. In + * such a case, one can call MIX_PlayTrack again to adjust parameters. * - * \since This function is available since SDL_mixer 3.0.0. + * The track will hold a reference to the provided MIX_Audio, so it is safe to + * call MIX_DestroyAudio() on it while the track is still using it. The track + * will drop its reference (and possibly free the resources) once it is no + * longer using the MIX_Audio. * - * \sa Mix_GetMusicDecoder - * \sa Mix_HasMusicDecoder - *} -function Mix_GetNumMusicDecoders(): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetNumMusicDecoders' {$ENDIF} {$ENDIF}; + * \param track the track on which to set a new audio input. + * \param audio the new audio input to set. May be nil. + * \returns true on success, false on error; call SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_SetTrackAudio(track: PMIX_Track; audio: PMIX_Audio): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackAudio' {$ENDIF} {$ENDIF}; -{** - * Get a music decoder's name. +{* + * Set a MIX_Track's input to an SDL_AudioStream. * - * The requested decoder's index must be between zero and - * Mix_GetNumMusicDecoders()-1. It's safe to call this with an invalid index; - * this function will return NULL in that case. + * Using an audio stream allows the application to generate any type of audio, + * in any format, possibly procedurally or on-demand, and mix in with all + * other tracks. * - * This list can change between builds AND runs of the program, if external - * libraries that add functionality become available. You must successfully - * call Mix_OpenAudio() before calling this function, as decoders are - * activated at device open time. + * When a track uses an audio stream, it will call SDL_GetAudioStreamData as + * it needs more audio to mix. The app can either buffer data to the stream + * ahead of time, or set a callback on the stream to provide data as needed. + * Please refer to SDL's documentation for details. * - * \param index index of the music decoder. - * \returns the music decoder's name. + * A given audio stream may only be assigned to a single track at a time; + * duplicate assignments won't return an error, but assigning a stream to + * multiple tracks will cause each track to read from the stream arbitrarily, + * causing confusion and incorrect mixing. * - * \since This function is available since SDL_mixer 3.0.0. + * Once a track has a valid input, it can start mixing sound by calling + * MIX_PlayTrack(), or possibly MIX_PlayTag(). * - * \sa Mix_GetNumMusicDecoders - *} -function Mix_GetMusicDecoder(index: cint): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicDecoder' {$ENDIF} {$ENDIF}; - -{** - * Check if a music decoder is available by name. + * Calling this function with a nil audio stream is legal, and removes any + * input from the track. If the track was currently playing, the next time the + * mixer runs, it'll notice this and mark the track as stopped, calling any + * assigned MIX_TrackStoppedCallback. * - * This result can change between builds AND runs of the program, if external - * libraries that add functionality become available. You must successfully - * call Mix_OpenAudio() before calling this function, as decoders are - * activated at device open time. + * It is legal to change the input of a track while it's playing, however some + * states, like loop points, may cease to make sense with the new audio. In + * such a case, one can call MIX_PlayTrack again to adjust parameters. * - * Decoder names are arbitrary but also obvious, so you have to know what - * you're looking for ahead of time, but usually it's the file extension in - * capital letters (some example names are "MOD", "MP3", "FLAC"). + * The provided audio stream must remain valid until the track no longer needs + * it (either by changing the track's input or destroying the track). * - * \param name the decoder name to query. - * \returns true if a decoder by that name is available, false otherwise. + * \param track the track on which to set a new audio input. + * \param stream the audio stream to use as the track's input. + * \returns true on success, false on error; call SDL_GetError() for details. * - * \since This function is available since SDL_mixer 3.0.0 + * \threadsafety It is safe to call this function from any thread. * - * \sa Mix_GetNumMusicDecoders - * \sa Mix_GetMusicDecoder - *} -function Mix_HasMusicDecoder(const name: PAnsiChar): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_HasMusicDecoder' {$ENDIF} {$ENDIF}; + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_SetTrackAudioStream(track: PMIX_Track; stream: PSDL_AudioStream): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackAudioStream' {$ENDIF} {$ENDIF}; -{** - * Find out the format of a mixer music. +{* + * Set a MIX_Track's input to an SDL_IOStream. * - * If `music` is NULL, this will query the currently playing music (and return - * MUS_NONE if nothing is currently playing). + * This is not the recommended way to set a track's input, but this can be + * useful for a very specific scenario: a large file, to be played once, that + * must be read from disk in small chunks as needed. In most cases, however, + * it is preferable to create a MIX_Audio ahead of time and use + * MIX_SetTrackAudio() instead. * - * \param music the music object to query, or NULL for the currently-playing - * music. - * \returns the Mix_MusicType for the music object. + * The stream supplied here should provide an audio file in a supported + * format. SDL_mixer will parse it during this call to make sure it's valid, + * and then will read file data from the stream as it needs to decode more + * during mixing. * - * \since This function is available since SDL_mixer 3.0.0 - *} -function Mix_GetMusicType(const music: PMix_Music): TMix_MusicType; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicType' {$ENDIF} {$ENDIF}; - -{** - * Get the title for a music object, or its filename. + * The stream must be able to seek through the complete set of data, or this + * function will fail. * - * This returns format-specific metadata. Not all file formats supply this! + * A given IOStream may only be assigned to a single track at a time; + * duplicate assignments won't return an error, but assigning a stream to + * multiple tracks will cause each track to read from the stream arbitrarily, + * causing confusion, incorrect mixing, or failure to decode. * - * If `music` is NULL, this will query the currently-playing music. + * Once a track has a valid input, it can start mixing sound by calling + * MIX_PlayTrack(), or possibly MIX_PlayTag(). * - * If music's title tag is missing or empty, the filename will be returned. If - * you'd rather have the actual metadata or nothing, use - * Mix_GetMusicTitleTag() instead. + * Calling this function with a nil stream is legal, and removes any input + * from the track. If the track was currently playing, the next time the mixer + * runs, it'll notice this and mark the track as stopped, calling any assigned + * MIX_TrackStoppedCallback. * - * Please note that if the music was loaded from an SDL_IOStream instead of a - * filename, the filename returned will be an empty string (""). + * It is legal to change the input of a track while it's playing, however some + * states, like loop points, may cease to make sense with the new audio. In + * such a case, one can call MIX_PlayTrack again to adjust parameters. * - * This function never returns NULL! If no data is available, it will return - * an empty string (""). + * The provided stream must remain valid until the track no longer needs it + * (either by changing the track's input or destroying the track). * - * \param music the music object to query, or NULL for the currently-playing - * music. - * \returns the music's title if available, or the filename if not, or "". + * \param track the track on which to set a new audio input. + * \param io the new i/o stream to use as the track's input. + * \param closeio if true, close the stream when done with it. + * \returns true on success, false on error; call SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_GetMusicTitleTag - * \sa Mix_GetMusicArtistTag - * \sa Mix_GetMusicAlbumTag - * \sa Mix_GetMusicCopyrightTag - *} -function Mix_GetMusicTitle(const music: PMix_Music): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicTitle' {$ENDIF} {$ENDIF}; + * \sa MIX_SetTrackRawIOStream + } +function MIX_SetTrackIOStream(track: PMIX_Track; io: PSDL_IOStream; closeio: Boolean): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackIOStream' {$ENDIF} {$ENDIF}; -{** - * Get the title for a music object. - * - * This returns format-specific metadata. Not all file formats supply this! +{* + * Set a MIX_Track's input to an SDL_IOStream providing raw PCM data. * - * If `music` is NULL, this will query the currently-playing music. + * This is not the recommended way to set a track's input, but this can be + * useful for a very specific scenario: a large file, to be played once, that + * must be read from disk in small chunks as needed. In most cases, however, + * it is preferable to create a MIX_Audio ahead of time and use + * MIX_SetTrackAudio() instead. * - * Unlike this function, Mix_GetMusicTitle() produce a string with the music's - * filename if a title isn't available, which might be preferable for some - * applications. + * Also, an MIX_SetTrackAudioStream() can _also_ provide raw PCM audio to a + * track, via an SDL_AudioStream, which might be preferable unless the data is + * already coming directly from an SDL_IOStream. * - * This function never returns NULL! If no data is available, it will return - * an empty string (""). + * The stream supplied here should provide an audio in raw PCM format. * - * \param music the music object to query, or NULL for the currently-playing - * music. - * \returns the music's title if available, or "". + * A given IOStream may only be assigned to a single track at a time; + * duplicate assignments won't return an error, but assigning a stream to + * multiple tracks will cause each track to read from the stream arbitrarily, + * causing confusion and incorrect mixing. * - * \since This function is available since SDL_mixer 3.0.0. + * Once a track has a valid input, it can start mixing sound by calling + * MIX_PlayTrack(), or possibly MIX_PlayTag(). * - * \sa Mix_GetMusicTitle - * \sa Mix_GetMusicArtistTag - * \sa Mix_GetMusicAlbumTag - * \sa Mix_GetMusicCopyrightTag - *} -function Mix_GetMusicTitleTag(const music: PMix_Music): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicTitleTag' {$ENDIF} {$ENDIF}; - -{** - * Get the artist name for a music object. + * Calling this function with a nil stream is legal, and removes any input + * from the track. If the track was currently playing, the next time the mixer + * runs, it'll notice this and mark the track as stopped, calling any assigned + * MIX_TrackStoppedCallback. * - * This returns format-specific metadata. Not all file formats supply this! + * It is legal to change the input of a track while it's playing, however some + * states, like loop points, may cease to make sense with the new audio. In + * such a case, one can call MIX_PlayTrack again to adjust parameters. * - * If `music` is NULL, this will query the currently-playing music. + * The provided stream must remain valid until the track no longer needs it + * (either by changing the track's input or destroying the track). * - * This function never returns NULL! If no data is available, it will return - * an empty string (""). + * \param track the track on which to set a new audio input. + * \param io the new i/o stream to use as the track's input. + * \param spec the format of the PCM data that the SDL_IOStream will provide. + * \param closeio if true, close the stream when done with it. + * \returns true on success, false on error; call SDL_GetError() for details. * - * \param music the music object to query, or NULL for the currently-playing - * music. - * \returns the music's artist name if available, or "". + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_GetMusicTitleTag - * \sa Mix_GetMusicAlbumTag - * \sa Mix_GetMusicCopyrightTag - *} -function Mix_GetMusicArtistTag(const music: PMix_Music): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicArtistTag' {$ENDIF} {$ENDIF}; + * \sa MIX_SetTrackAudioStream + * \sa MIX_SetTrackIOStream + } +function MIX_SetTrackRawIOStream(track: PMIX_Track; io: PSDL_IOStream; spec: PSDL_AudioSpec; closeio: Boolean): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackRawIOStream' {$ENDIF} {$ENDIF}; -{** - * Get the album name for a music object. +{* + * Assign an arbitrary tag to a track. + * + * A tag can be any valid C string in UTF-8 encoding. It can be useful to + * group tracks in various ways. For example, everything in-game might be + * marked as "game", so when the user brings up the settings menu, the app can + * pause all tracks involved in gameplay at once, but keep background music + * and menu sound effects running. * - * This returns format-specific metadata. Not all file formats supply this! + * A track can have as many tags as desired, until the machine runs out of + * memory. * - * If `music` is NULL, this will query the currently-playing music. + * It's legal to add the same tag to a track more than once; the extra + * attempts will report success but not change anything. * - * This function never returns NULL! If no data is available, it will return - * an empty string (""). + * Tags can later be removed with MIX_UntagTrack(). * - * \param music the music object to query, or NULL for the currently-playing - * music. - * \returns the music's album name if available, or "". + * \param track the track to add a tag to. + * \param tag the tag to add. + * \returns true on success, false on error; call SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_GetMusicTitleTag - * \sa Mix_GetMusicArtistTag - * \sa Mix_GetMusicCopyrightTag - *} -function Mix_GetMusicAlbumTag(const music: PMix_Music): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicAlbumTag' {$ENDIF} {$ENDIF}; + * \sa MIX_UntagTrack + } +function MIX_TagTrack(track: PMIX_Track; tag: PAnsiChar): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_TagTrack' {$ENDIF} {$ENDIF}; -{** - * Get the copyright text for a music object. +{* + * Remove an arbitrary tag from a track. + * + * A tag can be any valid C string in UTF-8 encoding. It can be useful to + * group tracks in various ways. For example, everything in-game might be + * marked as "game", so when the user brings up the settings menu, the app can + * pause all tracks involved in gameplay at once, but keep background music + * and menu sound effects running. * - * This returns format-specific metadata. Not all file formats supply this! + * It's legal to remove a tag that the track doesn't have; this function + * doesn't report errors, so this simply does nothing. * - * If `music` is NULL, this will query the currently-playing music. + * Specifying a nil tag will remove all tags on a track. * - * This function never returns NULL! If no data is available, it will return - * an empty string (""). + * \param track the track from which to remove a tag. + * \param tag the tag to remove, or nil to remove all current tags. * - * \param music the music object to query, or NULL for the currently-playing - * music. - * \returns the music's copyright text if available, or "". + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_GetMusicTitleTag - * \sa Mix_GetMusicArtistTag - * \sa Mix_GetMusicAlbumTag - *} -function Mix_GetMusicCopyrightTag(const music: PMix_Music): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicCopyrightTag' {$ENDIF} {$ENDIF}; + * \sa MIX_TagTrack + } +procedure MIX_UntagTrack(track: PMIX_Track; tag: PAnsiChar); cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_UntagTrack' {$ENDIF} {$ENDIF}; -type - TMix_MixCallback = procedure(udata: Pointer; stream: pcuint8; len: cint); cdecl; - PMix_MixCallback = ^TMix_MixCallback; - PPMix_MixCallback = ^PMix_MixCallback; - -{** - * Set a function that is called after all mixing is performed. +{* + * Get the tags currently associated with a track. * - * This can be used to provide real-time visual display of the audio stream or - * add a custom mixer filter for the stream data. + * Tags are not provided in any guaranteed order. * - * The callback will fire every time SDL_mixer is ready to supply more data to - * the audio device, after it has finished all its mixing work. This runs - * inside an SDL audio callback, so it's important that the callback return - * quickly, or there could be problems in the audio playback. + * \param track the track to query. + * \param count a Pointer filled in with the number of tags returned, can be + * nil. + * \returns an array of the tags, nil-terminated, or nil on failure; call + * SDL_GetError() for more information. This is a single allocation + * that should be freed with SDL_free() when it is no longer needed. * - * The data provided to the callback is in the format that the audio device - * was opened in, and it represents the exact waveform SDL_mixer has mixed - * from all playing chunks and music for playback. You are allowed to modify - * the data, but it cannot be resized (so you can't add a reverb effect that - * goes past the end of the buffer without saving some state between runs to - * add it into the next callback, or resample the buffer to a smaller size to - * speed it up, etc). + * \threadsafety It is safe to call this function from any thread. * - * The `arg` pointer supplied here is passed to the callback as-is, for - * whatever the callback might want to do with it (keep track of some ongoing - * state, settings, etc). + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_GetTrackTags(track: PMIX_Track; count: pcint):PPAnsiChar; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackTags' {$ENDIF} {$ENDIF}; + +{* + * Get all tracks with a specific tag. * - * Passing a NULL callback disables the post-mix callback until such a time as - * a new one callback is set. + * Tracks are not provided in any guaranteed order. * - * There is only one callback available. If you need to mix multiple inputs, - * be prepared to handle them from a single function. + * \param mixer the mixer to query. + * \param tag the tag to search. + * \param count a Pointer filled in with the number of tracks returned, can be + * nil. + * \returns an array of the tracks, nil-terminated, or nil on failure; call + * SDL_GetError() for more information. The returned Pointer should + * be freed with SDL_free() when it is no longer needed. * - * \param mix_func the callback function to become the new post-mix callback. - * \param arg a pointer that is passed, untouched, to the callback. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - * - * \sa Mix_HookMusic - *} -procedure Mix_SetPostMix(mix_func: TMix_MixCallback; arg: Pointer); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_SetPostMix' {$ENDIF} {$ENDIF}; + } +function MIX_GetTaggedTracks(mixer: PMIX_Mixer; tag: PAnsiChar; count: pcint):PPMIX_Track; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTaggedTracks' {$ENDIF} {$ENDIF}; -{** - * Add your own music player or additional mixer function. +{* + * Seek a playing track to a new position in its input. * - * This works something like Mix_SetPostMix(), but it has some crucial - * differences. Note that an app can use this _and_ Mix_SetPostMix() at the - * same time. This allows an app to replace the built-in music playback, - * either with it's own music decoder or with some sort of - * procedurally-generated audio output. + * (Not to be confused with MIX_SetTrack3DPosition(), which is positioning of + * the track in 3D space, not the playback position of its audio data.) * - * The supplied callback will fire every time SDL_mixer is preparing to supply - * more data to the audio device. This runs inside an SDL audio callback, so - * it's important that the callback return quickly, or there could be problems - * in the audio playback. + * On a playing track, the next time the mixer runs, it will start mixing from + * the new position. * - * Running this callback is the first thing SDL_mixer will do when starting to - * mix more audio. The buffer will contain silence upon entry, so the callback - * does not need to mix into existing data or initialize the buffer. + * Position is defined in _sample frames_ of decoded audio, not units of time, + * so that sample-perfect mixing can be achieved. To instead operate in units + * of time, use MIX_TrackMSToFrames() to get the approximate sample frames for + * a given tick. * - * Note that while a callback is set through this function, SDL_mixer will not - * mix any playing music; this callback is used instead. To disable this - * callback (and thus reenable built-in music playback) call this function - * with a NULL callback. + * This function requires an input that can seek (so it can not be used if the + * input was set with MIX_SetTrackAudioStream()), and a audio file format that + * allows seeking. SDL_mixer's decoders for some file formats do not offer + * seeking, or can only seek to times, not exact sample frames, in which case + * the final position may be off by some amount of sample frames. Please check + * your audio data and file bug reports if appropriate. * - * The data written to by the callback is in the format that the audio device - * was opened in, and upon return from the callback, SDL_mixer will mix any - * playing chunks (but not music!) into the buffer. The callback cannot resize - * the buffer (so you must be prepared to provide exactly the amount of data - * demanded or leave it as silence). + * It's legal to call this function on a track that is stopped, but a future + * call to MIX_PlayTrack() will reset the start position anyhow. Paused tracks + * will resume at the new input position. * - * The `arg` pointer supplied here is passed to the callback as-is, for - * whatever the callback might want to do with it (keep track of some ongoing - * state, settings, etc). + * \param track the track to change. + * \param frames the sample frame position to seek to. + * \returns true on success, false on error; call SDL_GetError() for details. * - * As there is only one music "channel" mixed, there is only one callback - * available. If you need to mix multiple inputs, be prepared to handle them - * from a single function. - * - * \param mix_func the callback function to become the new post-mix callback. - * \param arg a pointer that is passed, untouched, to the callback. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_SetPostMix - *} -procedure Mix_HookMusic(mix_func: TMix_MixCallback; arg: Pointer); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_HookMusic' {$ENDIF} {$ENDIF}; - -type - TMix_MusicFinishedCallback = procedure; cdecl; - PMix_MusicFinishedCallback = ^TMix_MusicFinishedCallback; - PPMix_MusicFinishedCallback = ^PMix_MusicFinishedCallback; + * \sa MIX_GetTrackPlaybackPosition + } +function MIX_SetTrackPlaybackPosition(track: PMIX_Track; frames: cint64): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackPlaybackPosition' {$ENDIF} {$ENDIF}; -{** - * Set a callback that runs when a music object has stopped playing. +{* + * Get the current input position of a playing track. + * + * (Not to be confused with MIX_GetTrack3DPosition(), which is positioning of + * the track in 3D space, not the playback position of its audio data.) * - * This callback will fire when the currently-playing music has completed, or - * when it has been explicitly stopped from a call to Mix_HaltMusic. As such, - * this callback might fire from an arbitrary background thread at almost any - * time; try to limit what you do here. + * Position is defined in _sample frames_ of decoded audio, not units of time, + * so that sample-perfect mixing can be achieved. To instead operate in units + * of time, use MIX_TrackFramesToMS() to convert the return value to + * milliseconds. * - * It is legal to start a new music object playing in this callback (or - * restart the one that just stopped). If the music finished normally, this - * can be used to loop the music without a gap in the audio playback. + * Stopped and paused tracks will report the position when they halted. + * Playing tracks will report the current position, which will change over + * time. * - * A NULL pointer will disable the callback. + * \param track the track to change. + * \returns the track's current sample frame position, or -1 on error; call + * SDL_GetError() for details. * - * \param music_finished the callback function to become the new notification - * mechanism. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_HookMusicFinished(music_finished: TMix_MusicFinishedCallback); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_HookMusic' {$ENDIF} {$ENDIF}; + * + * \sa MIX_SetTrackPlaybackPosition + } +function MIX_GetTrackPlaybackPosition(track: PMIX_Track): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackPlaybackPosition' {$ENDIF} {$ENDIF}; -{** - * Get a pointer to the user data for the current music hook. +{* + * Query whether a given track is fading. + * + * This specifically checks if the track is _not stopped_ (paused or playing), + * and it is fading in or out, and returns the number of frames remaining in + * the fade. + * + * If fading out, the returned value will be negative. When fading in, the + * returned value will be positive. If not fading, this function returns zero. * - * This returns the `arg` pointer last passed to Mix_HookMusic(), or NULL if - * that function has never been called. + * On various errors (MIX_Init() was not called, the track is nil), this + * returns 0, but there is no mechanism to distinguish errors from tracks that + * aren't fading. * - * \returns pointer to the user data previously passed to Mix_HookMusic. + * \param track the track to query. + * \returns less than 0 if the track is fading out, greater than 0 if fading + * in, zero otherwise. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetMusicHookData(): Pointer; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicHookData' {$ENDIF} {$ENDIF}; - -type - TMix_ChannelFinishedCallback = procedure(channel: cint); cdecl; - PMix_ChannelFinishedCallback = ^TMix_ChannelFinishedCallback; - PPMix_ChannelFinishedCallback = ^PMix_ChannelFinishedCallback; + } +function MIX_GetTrackFadeFrames(track: PMIX_Track): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackFadeFrames' {$ENDIF} {$ENDIF}; -{** - * Set a callback that runs when a channel has finished playing. +{* + * Query how many loops remain for a given track. + * + * This returns the number of loops still pending; if a track will eventually + * complete and loop to play again one more time, this will return 1. If a + * track _was_ looping but is on its final iteration of the loop (will stop + * when this iteration completes), this will return zero. * - * The callback may be called from the mixer's audio callback or it could be - * called as a result of Mix_HaltChannel(), etc. + * A track that is looping infinitely will return -1. This value does not + * report an error in this case. * - * The callback has a single parameter, `channel`, which says what mixer - * channel has just stopped. + * A track that is stopped (not playing and not paused) will have zero loops + * remaining. * - * A NULL pointer will disable the callback. + * On various errors (MIX_Init() was not called, the track is nil), this + * returns zero, but there is no mechanism to distinguish errors from + * non-looping tracks. * - * \param channel_finished the callback function to become the new - * notification mechanism. + * \param track the track to query. + * \returns the number of pending loops, zero if not looping, and -1 if + * looping infinitely. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_ChannelFinished(channel_finished: TMix_ChannelFinishedCallback); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_ChannelFinished' {$ENDIF} {$ENDIF}; + } +function MIX_GetTrackLoops(track: PMIX_Track): cint; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackLoops' {$ENDIF} {$ENDIF}; -{** - * Magic number for effects to operate on the postmix instead of a channel. - *} -const - MIX_CHANNEL_POST = -2; - -{** - * This is the format of a special effect callback: - * - * myeffect(int chan, void *stream, int len, void *udata); - * - * (chan) is the channel number that your effect is affecting. (stream) is the - * buffer of data to work upon. (len) is the size of (stream), and (udata) is - * a user-defined bit of data, which you pass as the last arg of - * Mix_RegisterEffect(), and is passed back unmolested to your callback. Your - * effect changes the contents of (stream) based on whatever parameters are - * significant, or just leaves it be, if you prefer. You can do whatever you - * like to the buffer, though, and it will continue in its changed state down - * the mixing pipeline, through any other effect functions, then finally to be - * mixed with the rest of the channels and music for the final output stream. - *} -type - TMix_EffectFunc_t = procedure(chan: cint; stream: Pointer; len: cint; udata: Pointer); cdecl; - PMix_EffectFunc_t = ^TMix_EffectFunc_t; - PPMix_EffectFunc_t = ^PMix_EffectFunc_t; - -{** - * This is a callback that signifies that a channel has finished all its loops - * and has completed playback. +{* + * Change the number of times a currently-playing track will loop. * - * This gets called if the buffer plays out normally, or if you call - * Mix_HaltChannel(), implicitly stop a channel via Mix_AllocateChannels(), or - * unregister a callback while it's still playing. - *} -type - TMix_EffectDone_t = procedure(chan: cint; udata: Pointer); cdecl; - PMix_EffectDone_t = ^TMix_EffectDone_t; - PPMix_EffectDone_t = ^PMix_EffectDone_t; - -{** - * Register a special effect function. - * - * At mixing time, the channel data is copied into a buffer and passed through - * each registered effect function. After it passes through all the functions, - * it is mixed into the final output stream. The copy to buffer is performed - * once, then each effect function performs on the output of the previous - * effect. Understand that this extra copy to a buffer is not performed if - * there are no effects registered for a given chunk, which saves CPU cycles, - * and any given effect will be extra cycles, too, so it is crucial that your - * code run fast. Also note that the data that your function is given is in - * the format of the sound device, and not the format you gave to - * Mix_OpenAudio(), although they may in reality be the same. This is an - * unfortunate but necessary speed concern. Use Mix_QuerySpec() to determine - * if you can handle the data before you register your effect, and take - * appropriate actions. - * - * You may also specify a callback (Mix_EffectDone_t) that is called when the - * channel finishes playing. This gives you a more fine-grained control than - * Mix_ChannelFinished(), in case you need to free effect-specific resources, - * etc. If you don't need this, you can specify NULL. - * - * You may set the callbacks before or after calling Mix_PlayChannel(). - * - * Things like Mix_SetPanning() are just internal special effect functions, so - * if you are using that, you've already incurred the overhead of a copy to a - * separate buffer, and that these effects will be in the queue with any - * functions you've registered. The list of registered effects for a channel - * is reset when a chunk finishes playing, so you need to explicitly set them - * with each call to Mix_PlayChannel*(). - * - * You may also register a special effect function that is to be run after - * final mixing occurs. The rules for these callbacks are identical to those - * in Mix_RegisterEffect, but they are run after all the channels and the - * music have been mixed into a single stream, whereas channel-specific - * effects run on a given channel before any other mixing occurs. These global - * effect callbacks are call "posteffects". Posteffects only have their - * Mix_EffectDone_t function called when they are unregistered (since the main - * output stream is never "done" in the same sense as a channel). You must - * unregister them manually when you've had enough. Your callback will be told - * that the channel being mixed is `MIX_CHANNEL_POST` if the processing is - * considered a posteffect. - * - * After all these effects have finished processing, the callback registered - * through Mix_SetPostMix() runs, and then the stream goes to the audio - * device. - * - * \param chan the channel to register an effect to, or MIX_CHANNEL_POST. - * \param f effect the callback to run when more of this channel is to be - * mixed. - * \param d effect done callback. - * \param arg argument. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * This replaces any previously-set remaining loops. A value of 1 will loop to + * the start of playback one time. Zero will not loop at all. A value of -1 + * requests infinite loops. If the input is not seekable and `num_loops` isn't + * zero, this function will report success but the track will stop at the + * point it should loop. + * + * The new loop count replaces any previous state, even if the track has + * already looped. + * + * This has no effect on a track that is stopped, or rather, starting a + * stopped track later will set a new loop count, replacing this value. + * Stopped tracks can specify a loop count while starting via + * MIX_PROP_PLAY_LOOPS_NUMBER. This function is intended to alter that count + * in the middle of playback. + * + * \param track the track to configure. + * \param num_loops new number of times to loop. Zero to disable looping, -1 + * to loop infinitely. + * \returns true on success, false on error; call SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_RegisterEffect( - chan: cint; - f: TMix_EffectFunc_t; - d: TMix_EffectDone_t; - arg: Pointer -): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_RegisterEffect' {$ENDIF} {$ENDIF}; + * + * \sa MIX_GetTrackLoops + } +function MIX_SetTrackLoops(track: PMIX_Track; num_loops: cint): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackLoops' {$ENDIF} {$ENDIF}; -{** - * Explicitly unregister a special effect function. +{* + * Query the MIX_Audio assigned to a track. * - * You may not need to call this at all, unless you need to stop an effect - * from processing in the middle of a chunk's playback. + * This returns the MIX_Audio object currently assigned to `track` through a + * call to MIX_SetTrackAudio(). If there is none assigned, or the track has an + * input that isn't a MIX_Audio (such as an SDL_AudioStream or SDL_IOStream), + * this will return nil. * - * Posteffects are never implicitly unregistered as they are for channels (as - * the output stream does not have an end), but they may be explicitly - * unregistered through this function by specifying MIX_CHANNEL_POST for a - * channel. + * On various errors (MIX_Init() was not called, the track is nil), this + * returns nil, but there is no mechanism to distinguish errors from tracks + * without a valid input. * - * \param channel the channel to unregister an effect on, or MIX_CHANNEL_POST. - * \param f effect the callback stop calling in future mixing iterations. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \param track the track to query. + * \returns a MIX_Audio if available, nil if not. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_UnregisterEffect(channel: cint; f: TMix_EffectFunc_t): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_UnregisterEffect' {$ENDIF} {$ENDIF}; + * + * \sa MIX_GetTrackAudioStream + } +function MIX_GetTrackAudio(track: PMIX_Track): PMIX_Audio; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackAudio' {$ENDIF} {$ENDIF}; -{** - * Explicitly unregister all special effect functions. +{* + * Query the SDL_AudioStream assigned to a track. * - * You may not need to call this at all, unless you need to stop all effects - * from processing in the middle of a chunk's playback. + * This returns the SDL_AudioStream object currently assigned to `track` + * through a call to MIX_SetTrackAudioStream(). If there is none assigned, or + * the track has an input that isn't an SDL_AudioStream (such as a MIX_Audio + * or SDL_IOStream), this will return nil. * - * Note that this will also shut off some internal effect processing, since - * Mix_SetPanning() and others may use this API under the hood. This is called - * internally when a channel completes playback. Posteffects are never - * implicitly unregistered as they are for channels, but they may be - * explicitly unregistered through this function by specifying - * MIX_CHANNEL_POST for a channel. + * On various errors (MIX_Init() was not called, the track is nil), this + * returns nil, but there is no mechanism to distinguish errors from tracks + * without a valid input. * - * \param channel the channel to unregister all effects on, or - * MIX_CHANNEL_POST. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \param track the track to query. + * \returns an SDL_AudioStream if available, nil if not. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_UnregisterAllEffects(channel: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_UnregisterAllEffects' {$ENDIF} {$ENDIF}; - -{** - * Environment variable that makes some mixing effects favor speed over - * quality. - *} -const - MIX_EFFECTSMAXSPEED = 'MIX_EFFECTSMAXSPEED'; - -(* - * These are the internally-defined mixing effects. They use the same API that - * effects defined in the application use, but are provided here as a - * convenience. Some effects can reduce their quality or use more memory in - * the name of speed; to enable this, make sure the environment variable - * MIX_EFFECTSMAXSPEED (see above) is defined before you call - * Mix_OpenAudio(). - *) + * + * \sa MIX_GetTrackAudio + } +function MIX_GetTrackAudioStream(track: PMIX_Track): PSDL_AudioStream; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackAudioStream' {$ENDIF} {$ENDIF}; -{** - * Set the panning of a channel. +{* + * Return the number of sample frames remaining to be mixed in a track. * - * The left and right channels are specified as integers between 0 and 255, - * quietest to loudest, respectively. + * If the track is playing or paused, and its total duration is known, this + * will report how much audio is left to mix. If the track is playing, future + * calls to this function will report different values. * - * Technically, this is just individual volume control for a sample with two - * (stereo) channels, so it can be used for more than just panning. If you - * want real panning, call it like this: + * Remaining audio is defined in _sample frames_ of decoded audio, not units + * of time, so that sample-perfect mixing can be achieved. To instead operate + * in units of time, use MIX_TrackFramesToMS() to convert the return value to + * milliseconds. * - * ```c - * Mix_SetPanning(channel, left, 255 - left); - * ``` + * This function does not take into account fade-outs or looping, just the + * current mixing position vs the duration of the track. * - * Setting `channel` to MIX_CHANNEL_POST registers this as a posteffect, and - * the panning will be done to the final mixed stream before passing it on to - * the audio device. + * If the duration of the track isn't known, or `track` is nil, this function + * returns -1. A stopped track reports 0. * - * This uses the Mix_RegisterEffect() API internally, and returns without - * registering the effect function if the audio device is not configured for - * stereo output. Setting both `left` and `right` to 255 causes this effect to - * be unregistered, since that is the data's normal state. + * \param track the track to query. + * \returns the total sample frames still to be mixed, or -1 if unknown. * - * Note that an audio device in mono mode is a no-op, but this call will - * return successful in that case. Error messages can be retrieved from - * Mix_GetError(). + * \threadsafety It is safe to call this function from any thread. * - * \param channel The mixer channel to pan or MIX_CHANNEL_POST. - * \param left Volume of stereo left channel, 0 is silence, 255 is full - * volume. - * \param right Volume of stereo right channel, 0 is silence, 255 is full - * volume. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_GetTrackRemaining(track: PMIX_Track): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackRemaining' {$ENDIF} {$ENDIF}; + +{* + * Convert milliseconds to sample frames for a track's current format. + * + * This calculates time based on the track's current input format, which can + * change when its input does, and also if that input changes formats + * mid-stream (for example, if decoding a file that is two MP3s concatenated + * together). + * + * On various errors (MIX_Init() was not called, the track is nil), this + * returns -1. If the track has no input, this returns -1. If `ms` is < 0, + * this returns -1. + * + * \param track the track to query. + * \param ms the milliseconds to convert to track-specific sample frames. + * \returns Converted number of sample frames, or -1 for errors/no input; call + * SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_SetPosition - * \sa Mix_SetDistance - *} -function Mix_SetPanning(channel: cint; left, right: cuint8): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_SetPanning' {$ENDIF} {$ENDIF}; + * \sa MIX_TrackFramesToMS + } +function MIX_TrackMSToFrames(track: PMIX_Track; ms: cint64): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_TrackMSToFrames' {$ENDIF} {$ENDIF}; -{** - * Set the position of a channel. - * - * `angle` is an integer from 0 to 360, that specifies the location of the - * sound in relation to the listener. `angle` will be reduced as necessary - * (540 becomes 180 degrees, -100 becomes 260). Angle 0 is due north, and - * rotates clockwise as the value increases. For efficiency, the precision of - * this effect may be limited (angles 1 through 7 might all produce the same - * effect, 8 through 15 are equal, etc). `distance` is an integer between 0 - * and 255 that specifies the space between the sound and the listener. The - * larger the number, the further away the sound is. Using 255 does not - * guarantee that the channel will be removed from the mixing process or be - * completely silent. For efficiency, the precision of this effect may be - * limited (distance 0 through 5 might all produce the same effect, 6 through - * 10 are equal, etc). Setting `angle` and `distance` to 0 unregisters this - * effect, since the data would be unchanged. - * - * If you need more precise positional audio, consider using OpenAL for - * spatialized effects instead of SDL_mixer. This is only meant to be a basic - * effect for simple "3D" games. - * - * If the audio device is configured for mono output, then you won't get any - * effectiveness from the angle; however, distance attenuation on the channel - * will still occur. While this effect will function with stereo voices, it - * makes more sense to use voices with only one channel of sound, so when they - * are mixed through this effect, the positioning will sound correct. You can - * convert them to mono through SDL before giving them to the mixer in the - * first place if you like. - * - * Setting the channel to MIX_CHANNEL_POST registers this as a posteffect, and - * the positioning will be done to the final mixed stream before passing it on - * to the audio device. - * - * This is a convenience wrapper over Mix_SetDistance() and Mix_SetPanning(). - * - * \param channel The mixer channel to position, or MIX_CHANNEL_POST. - * \param angle angle, in degrees. North is 0, and goes clockwise. - * \param distance distance; 0 is the listener, 255 is maxiumum distance away. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. +{* + * Convert sample frames for a track's current format to milliseconds. + * + * This calculates time based on the track's current input format, which can + * change when its input does, and also if that input changes formats + * mid-stream (for example, if decoding a file that is two MP3s concatenated + * together). + * + * Sample frames are more precise than milliseconds, so out of necessity, this + * function will approximate by rounding down to the closest full millisecond. + * + * On various errors (MIX_Init() was not called, the track is nil), this + * returns -1. If the track has no input, this returns -1. If `frames` is < 0, + * this returns -1. + * + * \param track the track to query. + * \param frames the track-specific sample frames to convert to milliseconds. + * \returns Converted number of milliseconds, or -1 for errors/no input; call + * SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_SetPosition(channel: cint; angle: cint16; distance: cuint8): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_SetPosition' {$ENDIF} {$ENDIF}; + * + * \sa MIX_TrackMSToFrames + } +function MIX_TrackFramesToMS(track: PMIX_Track; frames: cint64): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_TrackFramesToMS' {$ENDIF} {$ENDIF}; -{** - * Set the "distance" of a channel. - * - * `distance` is an integer from 0 to 255 that specifies the location of the - * sound in relation to the listener. Distance 0 is overlapping the listener, - * and 255 is as far away as possible. A distance of 255 does not guarantee - * silence; in such a case, you might want to try changing the chunk's volume, - * or just cull the sample from the mixing process with Mix_HaltChannel(). For - * efficiency, the precision of this effect may be limited (distances 1 - * through 7 might all produce the same effect, 8 through 15 are equal, etc). - * (distance) is an integer between 0 and 255 that specifies the space between - * the sound and the listener. The larger the number, the further away the - * sound is. Setting the distance to 0 unregisters this effect, since the data - * would be unchanged. If you need more precise positional audio, consider - * using OpenAL for spatialized effects instead of SDL_mixer. This is only - * meant to be a basic effect for simple "3D" games. - * - * Setting the channel to MIX_CHANNEL_POST registers this as a posteffect, and - * the distance attenuation will be done to the final mixed stream before - * passing it on to the audio device. - * - * This uses the Mix_RegisterEffect() API internally. - * - * \param channel The mixer channel to attenuate, or MIX_CHANNEL_POST. - * \param distance distance; 0 is the listener, 255 is maxiumum distance away. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. +{* + * Convert milliseconds to sample frames for a MIX_Audio's format. + * + * This calculates time based on the audio's initial format, even if the + * format would change mid-stream. + * + * If `ms` is < 0, this returns -1. + * + * \param audio the audio to query. + * \param ms the milliseconds to convert to audio-specific sample frames. + * \returns Converted number of sample frames, or -1 for errors/no input; call + * SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_SetDistance(channel: cint; distance: cuint8): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_SetDistance' {$ENDIF} {$ENDIF}; + * + * \sa MIX_AudioFramesToMS + } +function MIX_AudioMSToFrames(audio: PMIX_Audio; ms: cint64): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_AudioMSToFrames' {$ENDIF} {$ENDIF}; -{** - * Cause a channel to reverse its stereo. +{* + * Convert sample frames for a MIX_Audio's format to milliseconds. * - * This is handy if the user has his speakers hooked up backwards, or you - * would like to have a trippy sound effect. + * This calculates time based on the audio's initial format, even if the + * format would change mid-stream. * - * Calling this function with `flip` set to non-zero reverses the chunks's - * usual channels. If `flip` is zero, the effect is unregistered. + * Sample frames are more precise than milliseconds, so out of necessity, this + * function will approximate by rounding down to the closest full millisecond. * - * This uses the Mix_RegisterEffect() API internally, and thus is probably - * more CPU intensive than having the user just plug in his speakers - * correctly. Mix_SetReverseStereo() returns without registering the effect - * function if the audio device is not configured for stereo output. + * If `frames` is < 0, this returns -1. * - * If you specify MIX_CHANNEL_POST for `channel`, then this effect is used on - * the final mixed stream before sending it on to the audio device (a - * posteffect). + * \param audio the audio to query. + * \param frames the audio-specific sample frames to convert to milliseconds. + * \returns Converted number of milliseconds, or -1 for errors/no input; call + * SDL_GetError() for details. * - * \param channel The mixer channel to reverse, or MIX_CHANNEL_POST. - * \param flip non-zero to reverse stereo, zero to disable this effect. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. Note that an audio device in mono mode is a no-op, - * but this call will return successful in that case. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_SetReverseStereo(channel, flip: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_SetReverseStereo' {$ENDIF} {$ENDIF}; - -{** - * Reserve the first channels for the application. * - * While SDL_mixer will use up to the number of channels allocated by - * Mix_AllocateChannels(), this sets channels aside that will not be available - * when calling Mix_PlayChannel with a channel of -1 (play on the first unused - * channel). In this case, SDL_mixer will treat reserved channels as "used" - * whether anything is playing on them at the moment or not. - * - * This is useful if you've budgeted some channels for dedicated audio and the - * rest are just used as they are available. + * \sa MIX_AudioMSToFrames + } +function MIX_AudioFramesToMS(audio: PMIX_Audio; frames: cint64): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_AudioFramesToMS' {$ENDIF} {$ENDIF}; + +{* + * Convert milliseconds to sample frames at a specific sample rate. * - * Calling this function will set channels 0 to `n - 1` to be reserved. This - * will not change channel allocations. The number of reserved channels will - * be clamped to the current number allocated. + * If `sample_rate` is <= 0, this returns -1. If `ms` is < 0, this returns -1. * - * By default, no channels are reserved. + * \param sample_rate the sample rate to use for conversion. + * \param ms the milliseconds to convert to rate-specific sample frames. + * \returns Converted number of sample frames, or -1 for errors; call + * SDL_GetError() for details. * - * \param num number of channels to reserve, starting at index zero. - * \returns the number of reserved channels. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_ReserveChannels(num: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_ReserveChannels' {$ENDIF} {$ENDIF}; + * + * \sa MIX_FramesToMS + } +function MIX_MSToFrames(sample_rate: cint; ms: cint64): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_MSToFrames' {$ENDIF} {$ENDIF}; + +{* + * Convert sample frames, at a specific sample rate, to milliseconds. + * + * Sample frames are more precise than milliseconds, so out of necessity, this + * function will approximate by rounding down to the closest full millisecond. + * + * If `sample_rate` is <= 0, this returns -1. If `frames` is < 0, this returns + * -1. + * + * \param sample_rate the sample rate to use for conversion. + * \param frames the rate-specific sample frames to convert to milliseconds. + * \returns Converted number of milliseconds, or -1 for errors; call + * SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_MSToFrames + } +function MIX_FramesToMS(sample_rate: cint; frames: cint64): cint64; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_FramesToMS' {$ENDIF} {$ENDIF}; + +{ operations that deal with actual mixing/playback... } +{* + * Start (or restart) mixing a track for playback. + * + * The track will use whatever input was last assigned to it when playing; an + * input must be assigned to this track or this function will fail. Inputs are + * assigned with calls to MIX_SetTrackAudio(), MIX_SetTrackAudioStream(), or + * MIX_SetTrackIOStream(). + * + * If the track is already playing, or paused, this will restart the track + * with the newly-specified parameters. + * + * As there are several parameters, and more may be added in the future, they + * are specified with an SDL_PropertiesID. The parameters have reasonable + * defaults, and specifying a 0 for `options` will choose defaults for + * everything. + * + * SDL_PropertiesID are discussed in + * [SDL's documentation](https://wiki.libsdl.org/SDL3/CategoryProperties) + * . + * + * These are the supported properties: + * + * - `MIX_PROP_PLAY_LOOPS_NUMBER`: The number of times to loop the track when + * it reaches the end. A value of 1 will loop to the start one time. Zero + * will not loop at all. A value of -1 requests infinite loops. If the input + * is not seekable and this value isn't zero, this function will report + * success but the track will stop at the point it should loop. Default 0. + * - `MIX_PROP_PLAY_MAX_FRAME_NUMBER`: Mix at most to this sample frame + * position in the track. This will be treated as if the input reach EOF at + * this point in the audio file. If -1, mix all available audio without a + * limit. Default -1. + * - `MIX_PROP_PLAY_MAX_MILLISECONDS_NUMBER`: The same as using the + * MIX_PROP_PLAY_MAX_FRAME_NUMBER property, but the value is specified in + * milliseconds instead of sample frames. If both properties are specified, + * the sample frames value is favored. Default -1. + * - `MIX_PROP_PLAY_START_FRAME_NUMBER`: Start mixing from this sample frame + * position in the track's input. A value <= 0 will begin from the start of + * the track's input. If the input is not seekable and this value is > 0, + * this function will report failure. Default 0. + * - `MIX_PROP_PLAY_START_MILLISECOND_NUMBER`: The same as using the + * MIX_PROP_PLAY_START_FRAME_NUMBER property, but the value is specified in + * milliseconds instead of sample frames. If both properties are specified, + * the sample frames value is favored. Default 0. + * - `MIX_PROP_PLAY_LOOP_START_FRAME_NUMBER`: If the track is looping, this is + * the sample frame position that the track will loop back to; this lets one + * play an intro at the start of a track on the first iteration, but have a + * loop point somewhere in the middle thereafter. A value <= 0 will begin + * the loop from the start of the track's input. Default 0. + * - `MIX_PROP_PLAY_LOOP_START_MILLISECOND_NUMBER`: The same as using the + * MIX_PROP_PLAY_LOOP_START_FRAME_NUMBER property, but the value is + * specified in milliseconds instead of sample frames. If both properties + * are specified, the sample frames value is favored. Default 0. + * - `MIX_PROP_PLAY_FADE_IN_FRAMES_NUMBER`: The number of sample frames over + * which to fade in the newly-started track. The track will begin mixing + * silence and reach full volume smoothly over this many sample frames. If + * the track loops before the fade-in is complete, it will continue to fade + * correctly from the loop point. A value <= 0 will disable fade-in, so the + * track starts mixing at full volume. Default 0. + * - `MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER`: The same as using the + * MIX_PROP_PLAY_FADE_IN_FRAMES_NUMBER property, but the value is specified + * in milliseconds instead of sample frames. If both properties are + * specified, the sample frames value is favored. Default 0. + * - `MIX_PROP_PLAY_FADE_IN_START_GAIN_FLOAT`: If fading in, start fading from + * this volume level. 0.0f is silence and 1.0f is full volume, every in + * between is a linear change in gain. The specified value will be clamped + * between 0.0f and 1.0f. Default 0.0f. + * - `MIX_PROP_PLAY_APPEND_SILENCE_FRAMES_NUMBER`: At the end of mixing this + * track, after all loops are complete, append this many sample frames of + * silence as if it were part of the audio file. This allows for apps to + * implement effects in callbacks, like reverb, that need to generate + * samples past the end of the stream's audio, or perhaps introduce a delay + * before starting a new sound on the track without having to manage it + * directly. A value <= 0 generates no silence before stopping the track. + * Default 0. + * - `MIX_PROP_PLAY_APPEND_SILENCE_MILLISECONDS_NUMBER`: The same as using the + * MIX_PROP_PLAY_APPEND_SILENCE_FRAMES_NUMBER property, but the value is + * specified in milliseconds instead of sample frames. If both properties + * are specified, the sample frames value is favored. Default 0. + * - `MIX_PROP_PLAY_HALT_WHEN_EXHAUSTED_BOOLEAN`: If true, when input is + * completely consumed for the track, the mixer will mark the track as + * stopped (and call any appropriate MIX_TrackStoppedCallback, etc); to play + * more, the track will need to be restarted. If false, the track will just + * not contribute to the mix, but it will not be marked as stopped. There + * may be clever logic tricks this exposes generally, but this property is + * specifically useful when the track's input is an SDL_AudioStream assigned + * via MIX_SetTrackAudioStream(). Setting this property to true can be + * useful when pushing a complete piece of audio to the stream that has a + * definite ending, as the track will operate like any other audio was + * applied. Setting to false means as new data is added to the stream, the + * mixer will start using it as soon as possible, which is useful when audio + * should play immediately as it drips in: new VoIP packets, etc. Note that + * in this situation, if the audio runs out when needed, there _will_ be + * gaps in the mixed output, so try to buffer enough data to avoid this when + * possible. Note that a track is not consider exhausted until all its loops + * and appended silence have been mixed (and also, that loops don't mean + * anything when the input is an AudioStream). Default true. + * + * If this function fails, mixing of this track will not start (or restart, if + * it was already started). + * + * \param track the track to start (or restart) mixing. + * \param options a set of properties that control playback. May be zero. + * \returns true on success, false on error; call SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_PlayTag + * \sa MIX_PlayAudio + * \sa MIX_StopTrack + * \sa MIX_PauseTrack + * \sa MIX_TrackPlaying + } +function MIX_PlayTrack(track: PMIX_Track; options: TSDL_PropertiesID): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_PlayTrack' {$ENDIF} {$ENDIF}; -{** - * Assign a tag to a channel. +const + MIX_PROP_PLAY_LOOPS_NUMBER = 'SDL_mixer.play.loops'; + MIX_PROP_PLAY_MAX_FRAME_NUMBER = 'SDL_mixer.play.max_frame'; + MIX_PROP_PLAY_MAX_MILLISECONDS_NUMBER = 'SDL_mixer.play.max_milliseconds'; + MIX_PROP_PLAY_START_FRAME_NUMBER = 'SDL_mixer.play.start_frame'; + MIX_PROP_PLAY_START_MILLISECOND_NUMBER = 'SDL_mixer.play.start_millisecond'; + MIX_PROP_PLAY_LOOP_START_FRAME_NUMBER = 'SDL_mixer.play.loop_start_frame'; + MIX_PROP_PLAY_LOOP_START_MILLISECOND_NUMBER = 'SDL_mixer.play.loop_start_millisecond'; + MIX_PROP_PLAY_FADE_IN_FRAMES_NUMBER = 'SDL_mixer.play.fade_in_frames'; + MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER = 'SDL_mixer.play.fade_in_milliseconds'; + MIX_PROP_PLAY_FADE_IN_START_GAIN_FLOAT = 'SDL_mixer.play.fade_in_start_gain'; + MIX_PROP_PLAY_APPEND_SILENCE_FRAMES_NUMBER = 'SDL_mixer.play.append_silence_frames'; + MIX_PROP_PLAY_APPEND_SILENCE_MILLISECONDS_NUMBER = 'SDL_mixer.play.append_silence_milliseconds'; + MIX_PROP_PLAY_HALT_WHEN_EXHAUSTED_BOOLEAN = 'SDL_mixer.play.halt_when_exhausted'; + +{* + * Start (or restart) mixing all tracks with a specific tag for playback. * - * A tag is an arbitrary number that can be assigned to several mixer - * channels, to form groups of channels. + * This function follows all the same rules as MIX_PlayTrack(); please refer + * to its documentation for the details. Unlike that function, MIX_PlayTag() + * operates on multiple tracks at once that have the specified tag applied, + * via MIX_TagTrack(). * - * If 'tag' is -1, the tag is removed (actually -1 is the tag used to - * represent the group of all the channels). + * If all of your tagged tracks have different sample rates, it would make + * sense to use the `*_MILLISECONDS_NUMBER` properties in your `options`, + * instead of `*_FRAMES_NUMBER`, and let SDL_mixer figure out how to apply it + * to each track. * - * This function replaces the requested channel's current tag; you may only - * have one tag per channel. + * This function returns true if all tagged tracks are started (or restarted). + * If any track fails, this function returns false, but all tracks that could + * start will still be started even when this function reports failure. * - * You may not specify MAX_CHANNEL_POST for a channel. + * From the point of view of the mixing process, all tracks that successfully + * (re)start will do so at the exact same moment. * - * \param which the channel to set the tag on. - * \param tag an arbitrary value to assign a channel. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \param mixer the mixer on which to look for tagged tracks. + * \param tag the tag to use when searching for tracks. + * \param options the set of options that will be applied to each track. + * \returns true on success, false on error; call SDL_GetError() for details. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GroupChannel(which, tag: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GroupChannel' {$ENDIF} {$ENDIF}; - -{** - * Assign several consecutive channels to the same tag. + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. * - * A tag is an arbitrary number that can be assigned to several mixer - * channels, to form groups of channels. + * \sa MIX_PlayTrack + * \sa MIX_TagTrack + * \sa MIX_StopTrack + * \sa MIX_PauseTrack + * \sa MIX_TrackPlaying + } +function MIX_PlayTag(mixer: PMIX_Mixer; tag: PAnsiChar; options: TSDL_PropertiesID): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_PlayTag' {$ENDIF} {$ENDIF}; + +{* + * Play a MIX_Audio from start to finish without any management. * - * If 'tag' is -1, the tag is removed (actually -1 is the tag used to - * represent the group of all the channels). + * This is what we term a "fire-and-forget" sound. Internally, SDL_mixer will + * manage a temporary track to mix the specified MIX_Audio, cleaning it up + * when complete. No options can be provided for how to do the mixing, like + * MIX_PlayTrack() offers, and since the track is not available to the caller, + * no adjustments can be made to mixing over time. * - * This function replaces the requested channels' current tags; you may only - * have one tag per channel. + * This is not the function to build an entire game of any complexity around, + * but it can be convenient to play simple, one-off sounds that can't be + * stopped early. An example would be a voice saying "GAME OVER" during an + * unpausable endgame sequence. * - * You may not specify MAX_CHANNEL_POST for a channel. + * There are no limits to the number of fire-and-forget sounds that can mix at + * once (short of running out of memory), and SDL_mixer keeps an internal pool + * of temporary tracks it creates as needed and reuses when available. * - * Note that this returns success and failure in the _opposite_ way from - * Mix_GroupChannel(). We regret the API design mistake. + * \param mixer the mixer on which to play this audio. + * \param audio the audio input to play. + * \returns true if the track has begun mixing, false on error; call + * SDL_GetError() for details. * - * \param from the first channel to set the tag on. - * \param to_ the last channel to set the tag on, inclusive. - * \param tag an arbitrary value to assign a channel. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GroupChannels(from, to_, tag: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GroupChannels' {$ENDIF} {$ENDIF}; + * + * \sa MIX_PlayTrack + * \sa MIX_LoadAudio + } +function MIX_PlayAudio(mixer: PMIX_Mixer; audio: PMIX_Audio): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_PlayAudio' {$ENDIF} {$ENDIF}; -{** - * Finds the first available channel in a group of channels. +{* + * Halt a currently-playing track, possibly fading out over time. + * + * If `fade_out_frames` is > 0, the track does not stop mixing immediately, + * but rather fades to silence over that many sample frames before stopping. + * Sample frames are specific to the input assigned to the track, to allow for + * sample-perfect mixing. MIX_TrackMSToFrames() can be used to convert + * milliseconds to an appropriate value here. + * + * If the track ends normally while the fade-out is still in progress, the + * audio stops there; the fade is not adjusted to be shorter if it will last + * longer than the audio remaining. * - * A tag is an arbitrary number that can be assigned to several mixer - * channels, to form groups of channels. + * Once a track has completed any fadeout and come to a stop, it will call its + * MIX_TrackStoppedCallback, if any. It is legal to assign the track a new + * input and/or restart it during this callback. * - * This function searches all channels with a specified tag, and returns the - * channel number of the first one it finds that is currently unused. + * It is legal to halt a track that's already stopped. It does nothing, and + * returns true. * - * If no channels with the specified tag are unused, this function returns -1. + * \param track the track to halt. + * \param fade_out_frames the number of sample frames to spend fading out to + * silence before halting. 0 to stop immediately. + * \returns true if the track has stopped, false on error; call SDL_GetError() + * for details. * - * \param tag an arbitrary value, assigned to channels, to search for. - * \returns first available channel, or -1 if none are available. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GroupAvailable(tag: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GroupAvailable' {$ENDIF} {$ENDIF}; + * + * \sa MIX_PlayTrack + } +function MIX_StopTrack(track: PMIX_Track; fade_out_frames: cint64): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_StopTrack' {$ENDIF} {$ENDIF}; -{** - * Returns the number of channels in a group. +{* + * Halt all currently-playing tracks, possibly fading out over time. + * + * If `fade_out_ms` is > 0, the tracks do not stop mixing immediately, but + * rather fades to silence over that many milliseconds before stopping. Note + * that this is different than MIX_StopTrack(), which wants sample frames; + * this function takes milliseconds because different tracks might have + * different sample rates. + * + * If a track ends normally while the fade-out is still in progress, the audio + * stops there; the fade is not adjusted to be shorter if it will last longer + * than the audio remaining. * - * If tag is -1, this will return the total number of channels allocated, - * regardless of what their tag might be. + * Once a track has completed any fadeout and come to a stop, it will call its + * MIX_TrackStoppedCallback, if any. It is legal to assign the track a new + * input and/or restart it during this callback. This function does not + * prevent new play requests from being made. * - * \param tag an arbitrary value, assigned to channels, to search for. - * \returns the number of channels assigned the specified tag. + * \param mixer the mixer on which to stop all tracks. + * \param fade_out_ms the number of milliseconds to spend fading out to + * silence before halting. 0 to stop immediately. + * \returns true on success, false on error; call SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GroupCount(tag: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GroupCount' {$ENDIF} {$ENDIF}; + * + * \sa MIX_StopTrack + } +function MIX_StopAllTracks(mixer: PMIX_Mixer; fade_out_ms: cint64): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_StopAllTracks' {$ENDIF} {$ENDIF}; -{** - * Find the "oldest" sample playing in a group of channels. +{* + * Halt all tracks with a specific tag, possibly fading out over time. * - * Specifically, this function returns the channel number that is assigned the - * specified tag, is currently playing, and has the lowest start time, based - * on the value of SDL_GetTicks() when the channel started playing. + * If `fade_out_ms` is > 0, the tracks do not stop mixing immediately, but + * rather fades to silence over that many milliseconds before stopping. Note + * that this is different than MIX_StopTrack(), which wants sample frames; + * this function takes milliseconds because different tracks might have + * different sample rates. * - * If no channel with this tag is currently playing, this function returns -1. + * If a track ends normally while the fade-out is still in progress, the audio + * stops there; the fade is not adjusted to be shorter if it will last longer + * than the audio remaining. * - * \param tag an arbitrary value, assigned to channels, to search through. - * \returns the "oldest" sample playing in a group of channels. + * Once a track has completed any fadeout and come to a stop, it will call its + * MIX_TrackStoppedCallback, if any. It is legal to assign the track a new + * input and/or restart it during this callback. This function does not + * prevent new play requests from being made. + * + * \param mixer the mixer on which to stop tracks. + * \param tag the tag to use when searching for tracks. + * \param fade_out_ms the number of milliseconds to spend fading out to + * silence before halting. 0 to stop immediately. + * \returns true on success, false on error; call SDL_GetError() for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_GroupNewer - *} -function Mix_GroupOldest(tag: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GroupOldest' {$ENDIF} {$ENDIF}; + * \sa MIX_StopTrack + * \sa MIX_TagTrack + } +function MIX_StopTag(mixer: PMIX_Mixer; tag: PAnsiChar; fade_out_ms: cint64): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_StopTag' {$ENDIF} {$ENDIF}; -{** - * Find the "most recent" sample playing in a group of channels. +{* + * Pause a currently-playing track. * - * Specifically, this function returns the channel number that is assigned the - * specified tag, is currently playing, and has the highest start time, based - * on the value of SDL_GetTicks() when the channel started playing. + * A paused track is not considered "stopped," so its MIX_TrackStoppedCallback + * will not fire if paused, but it won't change state by default, generate + * audio, or generally make progress, until it is resumed. * - * If no channel with this tag is currently playing, this function returns -1. + * It is legal to pause a track that's in any state (playing, already paused, + * or stopped). Unless the track is currently playing, pausing does nothing, + * and returns true. A false return is only used to signal errors here (such + * as MIX_Init not being called or `track` being nil). * - * \param tag an arbitrary value, assigned to channels, to search through. - * \returns the "most recent" sample playing in a group of channels. + * \param track the track to pause. + * \returns true if the track has paused, false on error; call SDL_GetError() + * for details. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_GroupOldest - *} -function Mix_GroupNewer(tag: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GroupNewer' {$ENDIF} {$ENDIF}; + * \sa MIX_ResumeTrack + } +function MIX_PauseTrack(track: PMIX_Track): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_PauseTrack' {$ENDIF} {$ENDIF}; -{** - * Play an audio chunk on a specific channel. +{* + * Pause all currently-playing tracks. * - * If the specified channel is -1, play on the first free channel (and return - * -1 without playing anything new if no free channel was available). + * A paused track is not considered "stopped," so its MIX_TrackStoppedCallback + * will not fire if paused, but it won't change state by default, generate + * audio, or generally make progress, until it is resumed. * - * If a specific channel was requested, and there is a chunk already playing - * there, that chunk will be halted and the new chunk will take its place. + * This function makes all tracks on the specified mixer that are currently + * playing move to a paused state. They can later be resumed. * - * If `loops` is greater than zero, loop the sound that many times. If `loops` - * is -1, loop "infinitely" (~65000 times). + * \param mixer the mixer on which to pause all tracks. + * \returns true on success, false on error; call SDL_GetError() for details. * - * Note that before SDL_mixer 3.0.0, this function was a macro that called - * Mix_PlayChannelTimed() with a fourth parameter ("ticks") of -1. This - * function still does the same thing, but promotes it to a proper API - * function. Older binaries linked against a newer SDL_mixer will still call - * Mix_PlayChannelTimed directly, as they are using the macro, which was - * available since the dawn of time. + * \threadsafety It is safe to call this function from any thread. * - * \param channel the channel on which to play the new chunk. - * \param chunk the new chunk to play. - * \param loops the number of times the chunk should loop, -1 to loop (not - * actually) infinitely. - * \returns which channel was used to play the sound, or -1 if sound could not - * be played. + * \since This function is available since SDL_mixer 3.0.0. * - * \since This function is available since SDL_mixer 3.0.0 - *} -function Mix_PlayChannel(channel: cint; chunk: PMix_Chunk; loops: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_PlayChannel' {$ENDIF} {$ENDIF}; + * \sa MIX_ResumeTrack + * \sa MIX_ResumeAllTracks + } +function MIX_PauseAllTracks(mixer: PMIX_Mixer): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_PauseAllTracks' {$ENDIF} {$ENDIF}; -{** - * Play an audio chunk on a specific channel for a maximum time. +{* + * Pause all tracks with a specific tag. * - * If the specified channel is -1, play on the first free channel (and return - * -1 without playing anything new if no free channel was available). + * A paused track is not considered "stopped," so its MIX_TrackStoppedCallback + * will not fire if paused, but it won't change state by default, generate + * audio, or generally make progress, until it is resumed. * - * If a specific channel was requested, and there is a chunk already playing - * there, that chunk will be halted and the new chunk will take its place. + * This function makes all currently-playing tracks on the specified mixer, + * with a specific tag, move to a paused state. They can later be resumed. * - * If `loops` is greater than zero, loop the sound that many times. If `loops` - * is -1, loop "infinitely" (~65000 times). + * Tracks that match the specified tag that aren't currently playing are + * ignored. * - * `ticks` specifies the maximum number of milliseconds to play this chunk - * before halting it. If you want the chunk to play until all data has been - * mixed, specify -1. + * \param mixer the mixer on which to pause tracks. + * \param tag the tag to use when searching for tracks. + * \returns true on success, false on error; call SDL_GetError() for details. * - * Note that this function does not block for the number of ticks requested; - * it just schedules the chunk to play and notes the maximum for the mixer to - * manage later, and returns immediately. - * - * \param channel the channel on which to play the new chunk. - * \param chunk the new chunk to play. - * \param loops the number of times the chunk should loop, -1 to loop (not - * actually) infinitely. - * \param ticks the maximum number of milliseconds of this chunk to mix for - * playback. - * \returns which channel was used to play the sound, or -1 if sound could not - * be played. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_PlayChannelTimed(channel: cint; chunk: PMix_Chunk; loops, ticks: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_PlayChannelTimed' {$ENDIF} {$ENDIF}; + * + * \sa MIX_PauseTrack + * \sa MIX_ResumeTrack + * \sa MIX_ResumeTag + * \sa MIX_TagTrack + } +function MIX_PauseTag(mixer: PMIX_Mixer; tag: PAnsiChar): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_PauseTag' {$ENDIF} {$ENDIF}; -{** - * Play a new music object. +{* + * Resume a currently-paused track. * - * This will schedule the music object to begin mixing for playback. + * A paused track is not considered "stopped," so its MIX_TrackStoppedCallback + * will not fire if paused, but it won't change state by default, generate + * audio, or generally make progress, until it is resumed. * - * There is only ever one music object playing at a time; if this is called - * when another music object is playing, the currently-playing music is halted - * and the new music will replace it. + * It is legal to resume a track that's in any state (playing, paused, or + * stopped). Unless the track is currently paused, resuming does nothing, and + * returns true. A false return is only used to signal errors here (such as + * MIX_Init not being called or `track` being nil). * - * Please note that if the currently-playing music is in the process of fading - * out (via Mix_FadeOutMusic()), this function will *block* until the fade - * completes. If you need to avoid this, be sure to call Mix_HaltMusic() - * before starting new music. + * \param track the track to resume. + * \returns true if the track has resumed, false on error; call SDL_GetError() + * for details. * - * \param music the new music object to schedule for mixing. - * \param loops the number of loops to play the music for (0 means "play once - * and stop"). - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_PlayMusic(music: PMix_Music; loops: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_PlayMusic' {$ENDIF} {$ENDIF}; - -{** - * Play a new music object, fading in the audio. * - * This will start the new music playing, much like Mix_PlayMusic() will, but - * will start the music playing at silence and fade in to its normal volume - * over the specified number of milliseconds. + * \sa MIX_PauseTrack + } +function MIX_ResumeTrack(track: PMIX_Track): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_ResumeTrack' {$ENDIF} {$ENDIF}; + +{* + * Resume all currently-paused tracks. * - * If there is already music playing, that music will be halted and the new - * music object will take its place. + * A paused track is not considered "stopped," so its MIX_TrackStoppedCallback + * will not fire if paused, but it won't change state by default, generate + * audio, or generally make progress, until it is resumed. * - * If `loops` is greater than zero, loop the music that many times. If `loops` - * is -1, loop "infinitely" (~65000 times). + * This function makes all tracks on the specified mixer that are currently + * paused move to a playing state. * - * Fading music will change it's volume progressively, as if Mix_VolumeMusic() - * was called on it (which is to say: you probably shouldn't call - * Mix_VolumeMusic() on fading music). + * \param mixer the mixer on which to resume all tracks. + * \returns true on success, false on error; call SDL_GetError() for details. * - * \param music the new music object to play. - * \param loops the number of times the chunk should loop, -1 to loop (not - * actually) infinitely. - * \param ms the number of milliseconds to spend fading in. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_FadeInMusic(music: PMix_Music; loops, ms: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadeInMusic' {$ENDIF} {$ENDIF}; - -{** - * Play a new music object, fading in the audio, from a starting position. - * - * This will start the new music playing, much like Mix_PlayMusic() will, but - * will start the music playing at silence and fade in to its normal volume - * over the specified number of milliseconds. * - * If there is already music playing, that music will be halted and the new - * music object will take its place. - * - * If `loops` is greater than zero, loop the music that many times. If `loops` - * is -1, loop "infinitely" (~65000 times). + * \sa MIX_PauseTrack + * \sa MIX_PauseAllTracks + } +function MIX_ResumeAllTracks(mixer: PMIX_Mixer): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_ResumeAllTracks' {$ENDIF} {$ENDIF}; + +{* + * Resume all tracks with a specific tag. * - * Fading music will change it's volume progressively, as if Mix_VolumeMusic() - * was called on it (which is to say: you probably shouldn't call - * Mix_VolumeMusic() on fading music). + * A paused track is not considered "stopped," so its MIX_TrackStoppedCallback + * will not fire if paused, but it won't change state by default, generate + * audio, or generally make progress, until it is resumed. * - * This function allows the caller to start the music playback past the - * beginning of its audio data. You may specify a start position, in seconds, - * and the playback and fade-in will start there instead of with the first - * samples of the music. + * This function makes all currently-paused tracks on the specified mixer, + * with a specific tag, move to a playing state. * - * An app can specify a `position` of 0.0 to start at the beginning of the - * music (or just call Mix_FadeInMusic() instead). + * Tracks that match the specified tag that aren't currently paused are + * ignored. * - * To convert from milliseconds, divide by 1000.0. + * \param mixer the mixer on which to resume tracks. + * \param tag the tag to use when searching for tracks. + * \returns true on success, false on error; call SDL_GetError() for details. * - * \param music the new music object to play. - * \param loops the number of times the chunk should loop, -1 to loop (not - * actually) infinitely. - * \param ms the number of milliseconds to spend fading in. - * \param position the start position within the music, in seconds, where - * playback should start. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_FadeInMusicPos(music: PMix_Music; loops, ms: cint; position: cdouble): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadeInMusicPos' {$ENDIF} {$ENDIF}; + * + * \sa MIX_ResumeTrack + * \sa MIX_PauseTrack + * \sa MIX_PauseTag + * \sa MIX_TagTrack + } -{** - * Play an audio chunk on a specific channel, fading in the audio. - * - * This will start the new sound playing, much like Mix_PlayChannel() will, - * but will start the sound playing at silence and fade in to its normal - * volume over the specified number of milliseconds. - * - * If the specified channel is -1, play on the first free channel (and return - * -1 without playing anything new if no free channel was available). - * - * If a specific channel was requested, and there is a chunk already playing - * there, that chunk will be halted and the new chunk will take its place. - * - * If `loops` is greater than zero, loop the sound that many times. If `loops` - * is -1, loop "infinitely" (~65000 times). - * - * A fading channel will change it's volume progressively, as if Mix_Volume() - * was called on it (which is to say: you probably shouldn't call Mix_Volume() - * on a fading channel). - * - * Note that before SDL_mixer 3.0.0, this function was a macro that called - * Mix_FadeInChannelTimed() with a fourth parameter ("ticks") of -1. This - * function still does the same thing, but promotes it to a proper API - * function. Older binaries linked against a newer SDL_mixer will still call - * Mix_FadeInChannelTimed directly, as they are using the macro, which was - * available since the dawn of time. - * - * \param channel the channel on which to play the new chunk, or -1 to find - * any available. - * \param chunk the new chunk to play. - * \param loops the number of times the chunk should loop, -1 to loop (not - * actually) infinitely. - * \param ms the number of milliseconds to spend fading in. - * \returns which channel was used to play the sound, or -1 if sound could not - * be played. - * - * \since This function is available since SDL_mixer 3.0.0 - *} -function Mix_FadeInChannel(channel: cint; chunk: PMix_Chunk; loops, ms: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadeInChannel' {$ENDIF} {$ENDIF}; +function MIX_ResumeTag(mixer: PMIX_Mixer; tag: PAnsiChar): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_ResumeTag' {$ENDIF} {$ENDIF}; -{** - * Play an audio chunk on a specific channel, fading in the audio, for a - * maximum time. +{* + * Query if a track is currently playing. + * + * If this returns true, the track is currently contributing to the mixer's + * output (it's "playing"). It is not stopped nor paused. * - * This will start the new sound playing, much like Mix_PlayChannel() will, - * but will start the sound playing at silence and fade in to its normal - * volume over the specified number of milliseconds. + * On various errors (MIX_Init() was not called, the track is nil), this + * returns false, but there is no mechanism to distinguish errors from + * non-playing tracks. * - * If the specified channel is -1, play on the first free channel (and return - * -1 without playing anything new if no free channel was available). + * \param track the track to query. + * \returns true if playing, false otherwise. * - * If a specific channel was requested, and there is a chunk already playing - * there, that chunk will be halted and the new chunk will take its place. + * \threadsafety It is safe to call this function from any thread. * - * If `loops` is greater than zero, loop the sound that many times. If `loops` - * is -1, loop "infinitely" (~65000 times). + * \since This function is available since SDL_mixer 3.0.0. + * + * \sa MIX_PlayTrack + * \sa MIX_PauseTrack + * \sa MIX_ResumeTrack + * \sa MIX_StopTrack + * \sa MIX_TrackPaused + } +function MIX_TrackPlaying(track: PMIX_Track): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_TrackPlaying' {$ENDIF} {$ENDIF}; + +{* + * Query if a track is currently paused. * - * `ticks` specifies the maximum number of milliseconds to play this chunk - * before halting it. If you want the chunk to play until all data has been - * mixed, specify -1. + * If this returns true, the track is not currently contributing to the + * mixer's output but will when resumed (it's "paused"). It is not playing nor + * stopped. * - * Note that this function does not block for the number of ticks requested; - * it just schedules the chunk to play and notes the maximum for the mixer to - * manage later, and returns immediately. + * On various errors (MIX_Init() was not called, the track is nil), this + * returns false, but there is no mechanism to distinguish errors from + * non-playing tracks. * - * A fading channel will change it's volume progressively, as if Mix_Volume() - * was called on it (which is to say: you probably shouldn't call Mix_Volume() - * on a fading channel). + * \param track the track to query. + * \returns true if paused, false otherwise. * - * \param channel the channel on which to play the new chunk, or -1 to find - * any available. - * \param chunk the new chunk to play. - * \param loops the number of times the chunk should loop, -1 to loop (not - * actually) infinitely. - * \param ms the number of milliseconds to spend fading in. - * \param ticks the maximum number of milliseconds of this chunk to mix for - * playback. - * \returns which channel was used to play the sound, or -1 if sound could not - * be played. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_FadeInChannelTimed(channel: cint; chunk: PMix_Chunk; loops, ms, ticks: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadeInChannelTimed' {$ENDIF} {$ENDIF}; + * + * \sa MIX_PlayTrack + * \sa MIX_PauseTrack + * \sa MIX_ResumeTrack + * \sa MIX_StopTrack + * \sa MIX_TrackPlaying + } +function MIX_TrackPaused(track: PMIX_Track): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_TrackPaused' {$ENDIF} {$ENDIF}; -{** - * Set the volume for a specific channel. +{ volume control... } +{* + * Set a mixer's master gain control. * - * The volume must be between 0 (silence) and MIX_MAX_VOLUME (full volume). - * Note that MIX_MAX_VOLUME is 128. Values greater than MIX_MAX_VOLUME are - * clamped to MIX_MAX_VOLUME. + * Each mixer has a master gain, to adjust the volume of the entire mix. Each + * sample passing through the pipeline is modulated by this gain value. A gain + * of zero will generate silence, 1.0f will not change the mixed volume, and + * larger than 1.0f will increase the volume. Negative values are illegal. + * There is no maximum gain specified, but this can quickly get extremely + * loud, so please be careful with this setting. * - * Specifying a negative volume will not change the current volume; as such, - * this can be used to query the current volume without making changes, as - * this function returns the previous (in this case, still-current) value. + * A mixer's master gain defaults to 1.0f. * - * If the specified channel is -1, this function sets the volume for all - * channels, and returns _the average_ of all channels' volumes prior to this - * call. + * This value can be changed at any time to adjust the future mix. * - * The default volume for a channel is MIX_MAX_VOLUME (no attenuation). + * \param mixer the mixer to adjust. + * \param gain the new gain value. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \param channel the channel on set/query the volume on, or -1 for all - * channels. - * \param volume the new volume, between 0 and MIX_MAX_VOLUME, or -1 to query. - * \returns the previous volume. If the specified volume is -1, this returns - * the current volume. If `channel` is -1, this returns the average - * of all channels. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_Volume(channel, volume: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Volume' {$ENDIF} {$ENDIF}; - -{** - * Set the volume for a specific chunk. - * - * In addition to channels having a volume setting, individual chunks also - * maintain a separate volume. Both values are considered when mixing, so both - * affect the final attenuation of the sound. This lets an app adjust the - * volume for all instances of a sound in addition to specific instances of - * that sound. * - * The volume must be between 0 (silence) and MIX_MAX_VOLUME (full volume). - * Note that MIX_MAX_VOLUME is 128. Values greater than MIX_MAX_VOLUME are - * clamped to MIX_MAX_VOLUME. + * \sa MIX_GetMixerGain + * \sa MIX_SetTrackGain + } +function MIX_SetMixerGain(mixer: PMIX_Mixer; gain: cfloat): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetMixerGain' {$ENDIF} {$ENDIF}; + +{* + * Get a mixer's master gain control. * - * Specifying a negative volume will not change the current volume; as such, - * this can be used to query the current volume without making changes, as - * this function returns the previous (in this case, still-current) value. + * This returns the last value set through MIX_SetMixerGain(), or 1.0f if no + * value has ever been explicitly set. * - * The default volume for a chunk is MIX_MAX_VOLUME (no attenuation). + * \param mixer the mixer to query. + * \returns the mixer's current master gain. * - * \param chunk the chunk whose volume to adjust. - * \param volume the new volume, between 0 and MIX_MAX_VOLUME, or -1 to query. - * \returns the previous volume. If the specified volume is -1, this returns - * the current volume. If `chunk` is NULL, this returns -1. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_VolumeChunk(chunk: PMix_Chunk; volume: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_VolumeChunk' {$ENDIF} {$ENDIF}; - -{** - * Set the volume for the music channel. * - * The volume must be between 0 (silence) and MIX_MAX_VOLUME (full volume). - * Note that MIX_MAX_VOLUME is 128. Values greater than MIX_MAX_VOLUME are - * clamped to MIX_MAX_VOLUME. + * \sa MIX_SetMixerGain + * \sa MIX_GetTrackGain + } +function MIX_GetMixerGain(mixer: PMIX_Mixer): cfloat; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetMixerGain' {$ENDIF} {$ENDIF}; + +{* + * Set a track's gain control. * - * Specifying a negative volume will not change the current volume; as such, - * this can be used to query the current volume without making changes, as - * this function returns the previous (in this case, still-current) value. + * Each track has its own gain, to adjust its overall volume. Each sample from + * this track is modulated by this gain value. A gain of zero will generate + * silence, 1.0f will not change the mixed volume, and larger than 1.0f will + * increase the volume. Negative values are illegal. There is no maximum gain + * specified, but this can quickly get extremely loud, so please be careful + * with this setting. * - * The default volume for music is MIX_MAX_VOLUME (no attenuation). + * A track's gain defaults to 1.0f. * - * \param volume the new volume, between 0 and MIX_MAX_VOLUME, or -1 to query. - * \returns the previous volume. If the specified volume is -1, this returns - * the current volume. + * This value can be changed at any time to adjust the future mix. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_VolumeMusic(volume: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_VolumeMusic' {$ENDIF} {$ENDIF}; - -{** - * Query the current volume value for a music object. + * \param track the track to adjust. + * \param gain the new gain value. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \param music the music object to query. - * \returns the music's current volume, between 0 and MIX_MAX_VOLUME (128). + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetMusicVolume(music: PMix_Music): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicVolume' {$ENDIF} {$ENDIF}; - -{** - * Set the master volume for all channels. * - * SDL_mixer keeps a per-channel volume, a per-chunk volume, and a master - * volume, and considers all three when mixing audio. This function sets the - * master volume, which is applied to all playing channels when mixing. - * - * The volume must be between 0 (silence) and MIX_MAX_VOLUME (full volume). - * Note that MIX_MAX_VOLUME is 128. Values greater than MIX_MAX_VOLUME are - * clamped to MIX_MAX_VOLUME. + * \sa MIX_GetTrackGain + * \sa MIX_SetMixerGain + } +function MIX_SetTrackGain(track: PMIX_Track; gain: cfloat): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackGain' {$ENDIF} {$ENDIF}; + +{* + * Get a track's gain control. * - * Specifying a negative volume will not change the current volume; as such, - * this can be used to query the current volume without making changes, as - * this function returns the previous (in this case, still-current) value. + * This returns the last value set through MIX_SetTrackGain(), or 1.0f if no + * value has ever been explicitly set. * - * Note that the master volume does not affect any playing music; it is only - * applied when mixing chunks. Use Mix_VolumeMusic() for that. + * \param track the track to query. + * \returns the track's current gain. * - * \param volume the new volume, between 0 and MIX_MAX_VOLUME, or -1 to query. - * \returns the previous volume. If the specified volume is -1, this returns - * the current volume. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_MasterVolume(volume: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_MasterVolume' {$ENDIF} {$ENDIF}; + * + * \sa MIX_SetTrackGain + * \sa MIX_GetMixerGain + } +function MIX_GetTrackGain(track: PMIX_Track): cfloat; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackGain' {$ENDIF} {$ENDIF}; -{** - * Halt playing of a particular channel. +{* + * Set the gain control of all tracks with a specific tag. + * + * Each track has its own gain, to adjust its overall volume. Each sample from + * this track is modulated by this gain value. A gain of zero will generate + * silence, 1.0f will not change the mixed volume, and larger than 1.0f will + * increase the volume. Negative values are illegal. There is no maximum gain + * specified, but this can quickly get extremely loud, so please be careful + * with this setting. * - * This will stop further playback on that channel until a new chunk is - * started there. + * A track's gain defaults to 1.0f. * - * Specifying a channel of -1 will halt _all_ channels, except for any playing - * music. + * This will change the gain control on tracks on the specified mixer that + * have the specified tag. * - * Any halted channels will have any currently-registered effects - * deregistered, and will call any callback specified by Mix_ChannelFinished() - * before this function returns. + * From the point of view of the mixing process, all tracks that successfully + * change gain values will do so at the exact same moment. * - * You may not specify MAX_CHANNEL_POST for a channel. + * This value can be changed at any time to adjust the future mix. * - * \param channel channel to halt, or -1 to halt all channels. + * \param mixer the mixer on which to look for tagged tracks. + * \param tag the tag to use when searching for tracks. + * \param gain the new gain value. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_HaltChannel(channel: cint); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_HaltChannel' {$ENDIF} {$ENDIF}; + * + * \sa MIX_GetTrackGain + * \sa MIX_SetTrackGain + * \sa MIX_SetMixerGain + * \sa MIX_TagTrack + } -{** - * Halt playing of a group of channels by arbitrary tag. +function MIX_SetTagGain(mixer: PMIX_Mixer; tag: PAnsiChar; gain: cfloat): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTagGain' {$ENDIF} {$ENDIF}; + +{ frequency ratio ... } +{* + * Set a mixer's master frequency ratio. + * + * Each mixer has a master frequency ratio, that affects the entire mix. This + * can cause the final output to change speed and pitch. A value greater than + * 1.0f will play the audio faster, and at a higher pitch. A value less than + * 1.0f will play the audio slower, and at a lower pitch. 1.0f is normal + * speed. * - * This will stop further playback on all channels with a specific tag, until - * a new chunk is started there. + * Each track _also_ has a frequency ratio; it will be applied when mixing + * that track's audio regardless of the master setting. The master setting + * affects the final output after all mixing has been completed. * - * A tag is an arbitrary number that can be assigned to several mixer - * channels, to form groups of channels. + * A mixer's master frequency ratio defaults to 1.0f. * - * The default tag for a channel is -1. + * This value can be changed at any time to adjust the future mix. * - * Any halted channels will have any currently-registered effects - * deregistered, and will call any callback specified by Mix_ChannelFinished() - * before this function returns. + * \param mixer the mixer to adjust. + * \param ratio the frequency ratio. Must be between 0.01f and 100.0f. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \param tag an arbitrary value, assigned to channels, to search for. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_HaltGroup(tag: cint); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_HaltGroup' {$ENDIF} {$ENDIF}; + * + * \sa MIX_GetMixerFrequencyRatio + * \sa MIX_SetTrackFrequencyRatio + } +function MIX_SetMixerFrequencyRatio(mixer: PMIX_Mixer; ratio: cfloat): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetMixerFrequencyRatio' {$ENDIF} {$ENDIF}; -{** - * Halt playing of the music stream. +{* + * Get a mixer's master frequency ratio. + * + * This returns the last value set through MIX_SetMixerFrequencyRatio(), or + * 1.0f if no value has ever been explicitly set. * - * This will stop further playback of music until a new music object is - * started there. + * \param mixer the mixer to query. + * \returns the mixer's current master frequency ratio. * - * Any halted music will call any callback specified by - * Mix_HookMusicFinished() before this function returns. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_HaltMusic(); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_HaltMusic' {$ENDIF} {$ENDIF}; - -{** - * Change the expiration delay for a particular channel. * - * The channel will halt after the 'ticks' milliseconds have elapsed, or - * remove the expiration if 'ticks' is -1. + * \sa MIX_SetMixerFrequencyRatio + * \sa MIX_GetTrackFrequencyRatio + } +function MIX_GetMixerFrequencyRatio(mixer: PMIX_Mixer): cfloat; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetMixerFrequencyRatio' {$ENDIF} {$ENDIF}; + +{* + * Change the frequency ratio of a track. * - * This overrides the value passed to the fourth parameter of - * Mix_PlayChannelTimed(). + * The frequency ratio is used to adjust the rate at which audio data is + * consumed. Changing this effectively modifies the speed and pitch of the + * track's audio. A value greater than 1.0f will play the audio faster, and at + * a higher pitch. A value less than 1.0f will play the audio slower, and at a + * lower pitch. 1.0f is normal speed. * - * Specifying a channel of -1 will set an expiration for _all_ channels. + * The default value is 1.0f. * - * Any halted channels will have any currently-registered effects - * deregistered, and will call any callback specified by Mix_ChannelFinished() - * once the halt occurs. + * This value can be changed at any time to adjust the future mix. * - * Note that this function does not block for the number of ticks requested; - * it just schedules the chunk to expire and notes the time for the mixer to - * manage later, and returns immediately. + * \param track the track on which to change the frequency ratio. + * \param ratio the frequency ratio. Must be between 0.01f and 100.0f. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \param channel the channel to change the expiration time on. - * \param ticks number of milliseconds from now to let channel play before - * halting, -1 to not halt. - * \returns the number of channels that changed expirations. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_ExpireChannel(channel, ticks: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_ExpireChannel' {$ENDIF} {$ENDIF}; + * + * \sa MIX_GetTrackFrequencyRatio + } +function MIX_SetTrackFrequencyRatio(track: PMIX_Track; ratio: cfloat): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackFrequencyRatio' {$ENDIF} {$ENDIF}; -{** - * Halt a channel after fading it out for a specified time. +{* + * Query the frequency ratio of a track. * - * This will begin a channel fading from its current volume to silence over - * `ms` milliseconds. After that time, the channel is halted. + * The frequency ratio is used to adjust the rate at which audio data is + * consumed. Changing this effectively modifies the speed and pitch of the + * track's audio. A value greater than 1.0f will play the audio faster, and at + * a higher pitch. A value less than 1.0f will play the audio slower, and at a + * lower pitch. 1.0f is normal speed. * - * Any halted channels will have any currently-registered effects - * deregistered, and will call any callback specified by Mix_ChannelFinished() - * once the halt occurs. + * The default value is 1.0f. * - * A fading channel will change it's volume progressively, as if Mix_Volume() - * was called on it (which is to say: you probably shouldn't call Mix_Volume() - * on a fading channel). + * On various errors (MIX_Init() was not called, the track is nil), this + * returns 0.0f. Since this is not a valid value to set, this can be seen as + * an error state. * - * Note that this function does not block for the number of milliseconds - * requested; it just schedules the chunk to fade and notes the time for the - * mixer to manage later, and returns immediately. + * \param track the track on which to query the frequency ratio. + * \returns the current frequency ratio, or 0.0f on failure; call + * SDL_GetError() for more information. * - * \param which the channel to fade out. - * \param ms number of milliseconds to fade before halting the channel. - * \returns the number of channels scheduled to fade. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_FadeOutChannel(which, ms: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadeOutChannel' {$ENDIF} {$ENDIF}; + * + * \sa MIX_GetTrackFrequencyRatio + } +function MIX_GetTrackFrequencyRatio(track: PMIX_Track): cfloat; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrackFrequencyRatio' {$ENDIF} {$ENDIF}; -{** - * Halt a playing group of channels by arbitrary tag, after fading them out - * for a specified time. +{ channel maps... } +{* + * Set the current output channel map of a track. * - * This will begin fading a group of channels with a specific tag from their - * current volumes to silence over `ms` milliseconds. After that time, those - * channels are halted. + * Channel maps are optional; most things do not need them, instead passing + * data in the order that SDL expects. * - * A tag is an arbitrary number that can be assigned to several mixer - * channels, to form groups of channels. + * The output channel map reorders track data after transformations and before + * it is mixed into a mixer group. This can be useful for reversing stereo + * channels, for example. * - * The default tag for a channel is -1. + * Each item in the array represents an input channel, and its value is the + * channel that it should be remapped to. To reverse a stereo signal's left + * and right values, you'd have an array of ` 1, 0 `. It is legal to remap + * multiple channels to the same thing, so ` 1, 1 ` would duplicate the + * right channel to both channels of a stereo signal. An element in the + * channel map set to -1 instead of a valid channel will mute that channel, + * setting it to a silence value. * - * Any halted channels will have any currently-registered effects - * deregistered, and will call any callback specified by Mix_ChannelFinished() - * once the halt occurs. + * You cannot change the number of channels through a channel map, just + * reorder/mute them. * - * A fading channel will change it's volume progressively, as if Mix_Volume() - * was called on it (which is to say: you probably shouldn't call Mix_Volume() - * on a fading channel). + * Tracks default to no remapping applied. Passing a nil channel map is + * legal, and turns off remapping. * - * Note that this function does not block for the number of milliseconds - * requested; it just schedules the group to fade and notes the time for the - * mixer to manage later, and returns immediately. + * SDL_mixer will copy the channel map; the caller does not have to save this + * array after this call. + * + * \param track the track to change. + * \param chmap the new channel map, nil to reset to default. + * \param count The number of channels in the map. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \param tag an arbitrary value, assigned to channels, to search for. - * \param ms number of milliseconds to fade before halting the group. - * \returns the number of channels that were scheduled for fading. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_FadeOutGroup(tag, ms: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadeOutGroup' {$ENDIF} {$ENDIF}; + } -{** - * Halt the music stream after fading it out for a specified time. +function MIX_SetTrackOutputChannelMap(track: PMIX_Track; chmap: pcint; count: cint): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackOutputChannelMap' {$ENDIF} {$ENDIF}; + +{ positional audio... } +{* + * A set of per-channel gains for tracks using MIX_SetTrackStereo(). * - * This will begin the music fading from its current volume to silence over - * `ms` milliseconds. After that time, the music is halted. + * When forcing a track to stereo, the app can specify a per-channel gain, to + * further adjust the left or right outputs. * - * Any halted music will call any callback specified by - * Mix_HookMusicFinished() once the halt occurs. + * When mixing audio that has been forced to stereo, each channel is modulated + * by these values. A value of 1.0f produces no change, 0.0f produces silence. * - * Fading music will change it's volume progressively, as if Mix_VolumeMusic() - * was called on it (which is to say: you probably shouldn't call - * Mix_VolumeMusic() on a fading channel). + * A simple panning effect would be to set `left` to the desired value and + * `right` to `1.0f - left`. * - * Note that this function does not block for the number of milliseconds - * requested; it just schedules the music to fade and notes the time for the - * mixer to manage later, and returns immediately. + * \since This struct is available since SDL_mixer 3.0.0. * - * \param ms number of milliseconds to fade before halting the channel. - * \returns true if music was scheduled to fade, false otherwise. If no music - * is currently playing, this returns false. + * \sa MIX_SetTrackStereo + } +type + PPMIX_StereoGains = ^PMIX_StereoGains; + PMIX_StereoGains = ^TMIX_StereoGains; + TMIX_StereoGains = record + left: cfloat; {*< left channel gain } + right: cfloat; {*< right channel gain } + end; + +{* + * Force a track to stereo output, with optionally left/right panning. + * + * This will cause the output of the track to convert to stereo, and then mix + * it only onto the Front Left and Front Right speakers, regardless of the + * speaker configuration. The left and right channels are modulated by + * `gains`, which can be used to produce panning effects. This function may be + * called to adjust the gains at any time. + * + * If `gains` is not nil, this track will be switched into forced-stereo + * mode. If `gains` is nil, this will disable spatialization (both the + * forced-stereo mode of this function and full 3D spatialization of + * MIX_SetTrack3DPosition()). + * + * Negative gains are clamped to zero; there is no clamp for maximum, so one + * could set the value > 1.0f to make a channel louder. + * + * The track's 3D position, reported by MIX_GetTrack3DPosition(), will be + * reset to (0, 0, 0). + * + * \param track the track to adjust. + * \param gains the per-channel gains, or nil to disable spatialization. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_FadeOutMusic(ms: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadeOutMusic' {$ENDIF} {$ENDIF}; + * + * \sa MIX_SetTrack3DPosition + } +function MIX_SetTrackStereo(track: PMIX_Track; gains: PMIX_StereoGains): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackStereo' {$ENDIF} {$ENDIF}; -{** - * Query the fading status of the music stream. +{* + * 3D coordinates for MIX_SetTrack3DPosition. * - * This reports one of three values: + * The coordinates use a "right-handed" coordinate system, like OpenGL and + * OpenAL. * - * - `MIX_NO_FADING` - * - `MIX_FADING_OUT` - * - `MIX_FADING_IN` + * \since This struct is available since SDL_mixer 3.0.0. * - * If music is not currently playing, this returns `MIX_NO_FADING`. + * \sa MIX_SetTrack3DPosition + } +type + PPMIX_Point3D = ^PMIX_Point3D; + PMIX_Point3D = ^TMIX_Point3D; + TMIX_Point3D = record + x: cfloat; {*< X coordinate (negative left, positive right). } + y: cfloat; {*< Y coordinate (negative down, positive up). } + z: cfloat; {*< Z coordinate (negative forward, positive back). } + end; + +{* + * Set a track's position in 3D space. + * + * (Please note that SDL_mixer is not intended to be a extremely powerful 3D + * API. It lacks 3D features that other APIs like OpenAL offer: there's no + * doppler effect, distance models, rolloff, etc. This is meant to be Good + * Enough for games that can use some positional sounds and can even take + * advantage of surround-sound configurations.) + * + * If `position` is not nil, this track will be switched into 3D positional + * mode. If `position` is nil, this will disable positional mixing (both the + * full 3D spatialization of this function and forced-stereo mode of + * MIX_SetTrackStereo()). + * + * In 3D positional mode, SDL_mixer will mix this track as if it were + * positioned in 3D space, including distance attenuation (quieter as it gets + * further from the listener) and spatialization (positioned on the correct + * speakers to suggest direction, either with stereo outputs or full surround + * sound). + * + * For a mono speaker output, spatialization is effectively disabled but + * distance attenuation will still work, which is all you can really do with a + * single speaker. + * + * The coordinate system operates like OpenGL or OpenAL: a "right-handed" + * coordinate system. See MIX_Point3D for the details. + * + * The listener is always at coordinate (0,0,0) and can't be changed. + * + * The track's input will be converted to mono (1 channel) so it can be + * rendered across the correct speakers. + * + * \param track the track for which to set 3D position. + * \param position the new 3D position for the track. May be nil. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \returns the current fading status of the music stream. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_FadingMusic(): TMix_Fading; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadingMusic' {$ENDIF} {$ENDIF}; + * + * \sa MIX_GetTrack3DPosition + * \sa MIX_SetTrackStereo + } +function MIX_SetTrack3DPosition(track: PMIX_Track; position: PMIX_Point3D): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrack3DPosition' {$ENDIF} {$ENDIF}; -{** - * Query the fading status of a channel. +{* + * Get a track's current position in 3D space. + * + * If 3D positioning isn't enabled for this track, through a call to + * MIX_SetTrack3DPosition(), this will return (0,0,0). + * + * \param track the track to query. + * \param position on successful return, will contain the track's position. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * This reports one of three values: + * \threadsafety It is safe to call this function from any thread. * - * - `MIX_NO_FADING` - * - `MIX_FADING_OUT` - * - `MIX_FADING_IN` + * \since This function is available since SDL_mixer 3.0.0. * - * If nothing is currently playing on the channel, or an invalid channel is - * specified, this returns `MIX_NO_FADING`. + * \sa MIX_SetTrack3DPosition + } +function MIX_GetTrack3DPosition(track: PMIX_Track; position: PMIX_Point3D): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetTrack3DPosition' {$ENDIF} {$ENDIF}; + +{ Mix groups... } +{* + * Create a mixing group. * - * You may not specify MAX_CHANNEL_POST for a channel. + * Tracks are assigned to a mixing group (or if unassigned, they live in a + * mixer's internal default group). All tracks in a group are mixed together + * and the app can access this mixed data before it is mixed with all other + * groups to produce the final output. * - * You may not specify -1 for all channels; only individual channels may be - * queried. + * This can be a useful feature, but is completely optional; apps can ignore + * mixing groups entirely and still have a full experience with SDL_mixer. * - * \param which the channel to query. - * \returns the current fading status of the channel. + * After creating a group, assign tracks to it with MIX_SetTrackGroup(). Use + * MIX_SetGroupPostMixCallback() to access the group's mixed data. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_FadingChannel(which: cint): TMix_Fading; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_FadingChannel' {$ENDIF} {$ENDIF}; - -{** - * Pause a particular channel. + * A mixing group can be destroyed with MIX_DestroyGroup() when no longer + * needed. Destroying the mixer will also destroy all its still-existing + * mixing groups. * - * Pausing a channel will prevent further playback of the assigned chunk but - * will maintain the chunk's current mixing position. When resumed, this - * channel will continue to mix the chunk where it left off. + * \param mixer the mixer on which to create a mixing group. + * \returns a newly-created mixing group, or nil on error; call + * SDL_GetError() for more information. * - * A paused channel can be resumed by calling Mix_Resume(). + * \threadsafety It is safe to call this function from any thread. * - * A paused channel with an expiration will not expire while paused (the - * expiration countdown will be adjusted once resumed). + * \since This function is available since SDL_mixer 3.0.0. * - * It is legal to halt a paused channel. Playing a new chunk on a paused - * channel will replace the current chunk and unpause the channel. + * \sa MIX_DestroyGroup + * \sa MIX_SetTrackGroup + * \sa MIX_SetGroupPostMixCallback + } +function MIX_CreateGroup(mixer: PMIX_Mixer): PMIX_Group; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_CreateGroup' {$ENDIF} {$ENDIF}; + +{* + * Destroy a mixing group. * - * Specifying a channel of -1 will pause _all_ channels. Any music is - * unaffected. + * Any tracks currently assigned to this group will be reassigned to the + * mixer's internal default group. * - * You may not specify MAX_CHANNEL_POST for a channel. + * \param group the mixing group to destroy. * - * \param channel the channel to pause, or -1 to pause all channels. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_Pause(channel: cint); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Pause' {$ENDIF} {$ENDIF}; + * + * \sa MIX_CreateGroup + } +procedure MIX_DestroyGroup(group: PMIX_Group); cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_DestroyGroup' {$ENDIF} {$ENDIF}; -{** - * Pause playing of a group of channels by arbitrary tag. +{* + * Get the properties associated with a group. * - * Pausing a channel will prevent further playback of the assigned chunk but - * will maintain the chunk's current mixing position. When resumed, this - * channel will continue to mix the chunk where it left off. + * Currently SDL_mixer assigns no properties of its own to a group, but this + * can be a convenient place to store app-specific data. * - * A paused channel can be resumed by calling Mix_Resume() or - * Mix_ResumeGroup(). + * A SDL_PropertiesID is created the first time this function is called for a + * given group. * - * A paused channel with an expiration will not expire while paused (the - * expiration countdown will be adjusted once resumed). + * \param group the group to query. + * \returns a valid property ID on success or 0 on failure; call + * SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL_mixer 3.0.0. + } +function MIX_GetGroupProperties(group: PMIX_Group): TSDL_PropertiesID; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetGroupProperties' {$ENDIF} {$ENDIF}; + +{* + * Get the MIX_Mixer that owns a MIX_Group. * - * A tag is an arbitrary number that can be assigned to several mixer - * channels, to form groups of channels. + * This is the mixer Pointer that was passed to MIX_CreateGroup(). * - * The default tag for a channel is -1. + * \param group the group to query. + * \returns the mixer associated with the group, or nil on error; call + * SDL_GetError() for more information. * - * \param tag an arbitrary value, assigned to channels, to search for. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_PauseGroup(tag: cint); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_PauseGroup' {$ENDIF} {$ENDIF}; + } +function MIX_GetGroupMixer(group: PMIX_Group): PMIX_Mixer; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetGroupMixer' {$ENDIF} {$ENDIF}; -{** - * Resume a particular channel. +{* + * Assign a track to a mixing group. * - * It is legal to resume an unpaused or invalid channel; it causes no effect - * and reports no error. + * All tracks in a group are mixed together, and that output is made available + * to the app before it is mixed into the final output. * - * If the paused channel has an expiration, its expiration countdown resumes - * now, as well. + * Tracks can only be in one group at a time, and the track and group must + * have been created on the same MIX_Mixer. * - * Specifying a channel of -1 will resume _all_ paused channels. Any music is - * unaffected. + * Setting a track to a nil group will remove it from any app-created groups, + * and reassign it to the mixer's internal default group. + * + * \param track the track to set mixing group assignment. + * \param group the new mixing group to assign to. May be nil. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \param channel the channel to resume, or -1 to resume all paused channels. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_Resume(channel: cint); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Resume' {$ENDIF} {$ENDIF}; + * + * \sa MIX_CreateGroup + * \sa MIX_SetGroupPostMixCallback + } +function MIX_SetTrackGroup(track: PMIX_Track; group: PMIX_Group): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackGroup' {$ENDIF} {$ENDIF}; -{** - * Resume playing of a group of channels by arbitrary tag. +{ Hooks... } +{* + * A callback that fires when a MIX_Track is stopped. * - * It is legal to resume an unpaused or invalid channel; it causes no effect - * and reports no error. + * This callback is fired when a track completes playback, either because it + * ran out of data to mix (and all loops were completed as well), or it was + * explicitly stopped by the app. Pausing a track will not fire this callback. * - * If the paused channel has an expiration, its expiration countdown resumes - * now, as well. + * It is legal to adjust the track, including changing its input and + * restarting it. If this is done because it ran out of data in the middle of + * mixing, the mixer will start mixing the new track state in its current run + * without any gap in the audio. * - * A tag is an arbitrary number that can be assigned to several mixer - * channels, to form groups of channels. + * This callback will not fire when a playing track is destroyed. * - * The default tag for a channel is -1. + * \param userdata an opaque Pointer provided by the app for its personal use. + * \param track the track that has stopped. * - * \param tag an arbitrary value, assigned to channels, to search for. + * \since This datatype is available since SDL_mixer 3.0.0. * - * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_ResumeGroup(tag: cint); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_ResumeGroup' {$ENDIF} {$ENDIF}; + * \sa MIX_SetTrackStoppedCallback + } +type + TMIX_TrackStoppedCallback = procedure (userdata: Pointer; track: PMIX_Track); cdecl; -{** - * Query whether a particular channel is paused. +{* + * Set a callback that fires when a MIX_Track is stopped. * - * If an invalid channel is specified, this function returns zero. + * When a track completes playback, either because it ran out of data to mix + * (and all loops were completed as well), or it was explicitly stopped by the + * app, it will fire the callback specified here. * - * \param channel the channel to query, or -1 to query all channels. - * \return 1 if channel paused, 0 otherwise. If `channel` is -1, returns the - * number of paused channels. + * Each track has its own unique callback. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_Paused(channel: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Paused' {$ENDIF} {$ENDIF}; - -{** - * Pause the music stream. + * Passing a nil callback here is legal; it disables this track's callback. * - * Pausing the music stream will prevent further playback of the assigned - * music object, but will maintain the object's current mixing position. When - * resumed, this channel will continue to mix the music where it left off. + * Pausing a track will not fire the callback, nor will the callback fire on a + * playing track that is being destroyed. * - * Paused music can be resumed by calling Mix_ResumeMusic(). + * It is legal to adjust the track, including changing its input and + * restarting it. If this is done because it ran out of data in the middle of + * mixing, the mixer will start mixing the new track state in its current run + * without any gap in the audio. * - * It is legal to halt paused music. Playing a new music object when music is - * paused will replace the current music and unpause the music stream. + * \param track the track to assign this callback to. + * \param cb the function to call when the track stops. May be nil. + * \param userdata an opaque Pointer provided to the callback for its own + * personal use. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_PauseMusic(); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_PauseMusic' {$ENDIF} {$ENDIF}; + * + * \sa MIX_TrackStoppedCallback + } +function MIX_SetTrackStoppedCallback(track: PMIX_Track; cb: TMIX_TrackStoppedCallback; userdata: Pointer): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackStoppedCallback' {$ENDIF} {$ENDIF}; -{** - * Resume the music stream. +{* + * A callback that fires when a MIX_Track is mixing at various stages. * - * It is legal to resume an unpaused music stream; it causes no effect and - * reports no error. + * This callback is fired for different parts of the mixing pipeline, and + * gives the app visbility into the audio data that is being generated at + * various stages. * - * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_ResumeMusic(); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_ResumeMusic' {$ENDIF} {$ENDIF}; - -{** - * Rewind the music stream. + * The audio data passed through here is _not_ const data; the app is + * permitted to change it in any way it likes, and those changes will + * propagate through the mixing pipeline. * - * This causes the currently-playing music to start mixing from the beginning - * of the music, as if it were just started. + * An audiospec is provided. Different tracks might be in different formats, + * and an app needs to be able to handle that, but SDL_mixer always does its + * mixing work in 32-bit float samples, even if the inputs or final output are + * not floating point. As such, `spec->format` will always be `SDL_AUDIO_F32` + * and `pcm` hardcoded to be a float Pointer. * - * It's a legal no-op to rewind the music stream when not playing. + * `samples` is the number of float values pointed to by `pcm`: samples, not + * sample frames! There are no promises how many samples will be provided + * per-callback, and this number can vary wildly from call to call, depending + * on many factors. * - * \since This function is available since SDL_mixer 3.0.0. - *} -procedure Mix_RewindMusic(); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_RewindMusic' {$ENDIF} {$ENDIF}; - -{** - * Query whether the music stream is paused. + * Making changes to the track during this callback is undefined behavior. + * Change the data in `pcm` but not the track itself. * - * \return true if music is paused, false otherwise. + * \param userdata an opaque Pointer provided by the app for its personal use. + * \param track the track that is being mixed. + * \param spec the format of the data in `pcm`. + * \param pcm the raw PCM data in float32 format. + * \param samples the number of float values pointed to by `pcm`. * - * \since This function is available since SDL_mixer 3.0.0. + * \since This datatype is available since SDL_mixer 3.0.0. * - * \sa Mix_PauseMusic - * \sa Mix_ResumeMusic - *} -function Mix_PausedMusic(): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_PausedMusic' {$ENDIF} {$ENDIF}; + * \sa MIX_SetTrackRawCallback + * \sa MIX_SetTrackCookedCallback + } +type + TMIX_TrackMixCallback = procedure (userdata: Pointer; track: PMIX_Track; spec: PSDL_AudioSpec; pcm: pcfloat; samples: cint); cdecl; -{** - * Jump to a given order in mod music. +{* + * Set a callback that fires when a MIX_Track has initial decoded audio. * - * This only applies to MOD music formats. + * As a track needs to mix more data, it pulls from its input (a MIX_Audio, an + * SDL_AudioStream, etc). This input might be a compressed file format, like + * MP3, so a little more data is uncompressed from it. * - * \param order order. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * Once the track has PCM data to start operating on, it can fire a callback + * before _any_ changes to the raw PCM input have happened. This lets an app + * view the data before it has gone through transformations such as gain, 3D + * positioning, fading, etc. It can also change the data in any way it pleases + * during this callback, and the mixer will continue as if this data came + * directly from the input. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_ModMusicJumpToOrder(order: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_ModMusicJumpToOrder' {$ENDIF} {$ENDIF}; - -{** - * Start a track in music object. + * Each track has its own unique raw callback. * - * This only applies to GME music formats. + * Passing a nil callback here is legal; it disables this track's callback. * - * \param music the music object. - * \param track the track number to play. 0 is the first track. + * \param track the track to assign this callback to. + * \param cb the function to call when the track mixes. May be nil. + * \param userdata an opaque Pointer provided to the callback for its own + * personal use. * \returns true on success or false on failure; call SDL_GetError() for more * information. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_StartTrack(music: PMix_Music; track: cint): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_StartTrack' {$ENDIF} {$ENDIF}; + * + * \sa MIX_TrackMixCallback + * \sa MIX_SetTrackCookedCallback + } +function MIX_SetTrackRawCallback(track: PMIX_Track; cb: TMIX_TrackMixCallback; userdata: Pointer): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackRawCallback' {$ENDIF} {$ENDIF}; -{** - * Get number of tracks in music object. +{* + * Set a callback that fires when the mixer has transformed a track's audio. * - * This only applies to GME music formats. + * As a track needs to mix more data, it pulls from its input (a MIX_Audio, an + * SDL_AudioStream, etc). This input might be a compressed file format, like + * MP3, so a little more data is uncompressed from it. * - * \param music the music object. - * \returns number of tracks if successful, or -1 if failed or isn't - * implemented. + * Once the track has PCM data to start operating on, and its raw callback has + * completed, it will begin to transform the audio: gain, fading, frequency + * ratio, 3D positioning, etc. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetNumTracks(music: PMix_Music): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetNumTracks' {$ENDIF} {$ENDIF}; - -{** - * Set the current position in the music stream, in seconds. + * A callback can be fired after all these transformations, but before the + * transformed data is mixed into other tracks. This lets an app view the data + * at the last moment that it is still a part of this track. It can also + * change the data in any way it pleases during this callback, and the mixer + * will continue as if this data came directly from the input. * - * To convert from milliseconds, divide by 1000.0. + * Each track has its own unique cooked callback. * - * This function is only implemented for MOD music formats (set pattern order - * number) and for WAV, OGG, FLAC, MP3, and MOD music at the moment. + * Passing a nil callback here is legal; it disables this track's callback. * - * \param position the new position, in seconds (as a double). + * \param track the track to assign this callback to. + * \param cb the function to call when the track mixes. May be nil. + * \param userdata an opaque Pointer provided to the callback for its own + * personal use. * \returns true on success or false on failure; call SDL_GetError() for more * information. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_SetMusicPosition(position: cdouble): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_SetMusicPosition' {$ENDIF} {$ENDIF}; + * + * \sa MIX_TrackMixCallback + * \sa MIX_SetTrackRawCallback + } +function MIX_SetTrackCookedCallback(track: PMIX_Track; cb: TMIX_TrackMixCallback; userdata: Pointer): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetTrackCookedCallback' {$ENDIF} {$ENDIF}; -{** - * Get the time current position of music stream, in seconds. +{* + * A callback that fires when a MIX_Group has completed mixing. * - * To convert to milliseconds, multiply by 1000.0. + * This callback is fired when a mixing group has finished mixing: all tracks + * in the group have mixed into a single buffer and are prepared to be mixed + * into all other groups for the final mix output. * - * \param music the music object to query. - * \returns -1.0 if this feature is not supported for some codec. + * The audio data passed through here is _not_ const data; the app is + * permitted to change it in any way it likes, and those changes will + * propagate through the mixing pipeline. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetMusicPosition(music: PMix_Music): cdouble; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicPosition' {$ENDIF} {$ENDIF}; - -{** - * Get a music object's duration, in seconds. + * An audiospec is provided. Different groups might be in different formats, + * and an app needs to be able to handle that, but SDL_mixer always does its + * mixing work in 32-bit float samples, even if the inputs or final output are + * not floating point. As such, `spec->format` will always be `SDL_AUDIO_F32` + * and `pcm` hardcoded to be a float Pointer. * - * To convert to milliseconds, multiply by 1000.0. + * `samples` is the number of float values pointed to by `pcm`: samples, not + * sample frames! There are no promises how many samples will be provided + * per-callback, and this number can vary wildly from call to call, depending + * on many factors. * - * If NULL is passed, returns duration of current playing music. + * \param userdata an opaque Pointer provided by the app for its personal use. + * \param group the group that is being mixed. + * \param spec the format of the data in `pcm`. + * \param pcm the raw PCM data in float32 format. + * \param samples the number of float values pointed to by `pcm`. * - * \param music the music object to query. - * \returns music duration in seconds, or -1.0 on error. + * \since This datatype is available since SDL_mixer 3.0.0. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_MusicDuration(music: PMix_Music): cdouble; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_MusicDuration' {$ENDIF} {$ENDIF}; + * \sa MIX_SetGroupPostMixCallback + } +type + TMIX_GroupMixCallback = procedure (userdata: Pointer; group: PMIX_Group; spec: PSDL_AudioSpec; pcm: pcfloat; samples: cint); cdecl; -{** - * Get the loop start time position of music stream, in seconds. +{* + * Set a callback that fires when a mixer group has completed mixing. + * + * After all playing tracks in a mixer group have pulled in more data from + * their inputs, transformed it, and mixed together into a single buffer, a + * callback can be fired. This lets an app view the data at the last moment + * that it is still a part of this group. It can also change the data in any + * way it pleases during this callback, and the mixer will continue as if this + * data came directly from the group's mix buffer. * - * To convert to milliseconds, multiply by 1000.0. + * Each group has its own unique callback. Tracks that aren't in an explicit + * MIX_Group are mixed in an internal grouping that is not available to the + * app. * - * If NULL is passed, returns duration of current playing music. + * Passing a nil callback here is legal; it disables this group's callback. * - * \param music the music object to query. - * \returns -1.0 if this feature is not used for this music or not supported - * for some codec. + * \param group the mixing group to assign this callback to. + * \param cb the function to call when the group mixes. May be nil. + * \param userdata an opaque Pointer provided to the callback for its own + * personal use. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetMusicLoopStartTime(music: PMix_Music): cdouble; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicLoopStartTime' {$ENDIF} {$ENDIF}; - -{** - * Get the loop end time position of music stream, in seconds. * - * To convert to milliseconds, multiply by 1000.0. + * \sa MIX_GroupMixCallback + } +function MIX_SetGroupPostMixCallback(group: PMIX_Group; cb: TMIX_GroupMixCallback; userdata: Pointer): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetGroupPostMixCallback' {$ENDIF} {$ENDIF}; + +{* + * A callback that fires when all mixing has completed. * - * If NULL is passed, returns duration of current playing music. + * This callback is fired when the mixer has completed all its work. If this + * mixer was created with MIX_CreateMixerDevice(), the data provided by this + * callback is what is being sent to the audio hardware, minus last + * conversions for format requirements. If this mixer was created with + * MIX_CreateMixer(), this is what is being output from MIX_Generate(), after + * final conversions. * - * \param music the music object to query. - * \returns -1.0 if this feature is not used for this music or not supported - * for some codec. + * The audio data passed through here is _not_ const data; the app is + * permitted to change it in any way it likes, and those changes will replace + * the final mixer pipeline output. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetMusicLoopEndTime(music: PMix_Music): cdouble; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicLoopEndTime' {$ENDIF} {$ENDIF}; - -{** - * Get the loop time length of music stream, in seconds. + * An audiospec is provided. SDL_mixer always does its mixing work in 32-bit + * float samples, even if the inputs or final output are not floating point. + * As such, `spec->format` will always be `SDL_AUDIO_F32` and `pcm` hardcoded + * to be a float Pointer. * - * To convert to milliseconds, multiply by 1000.0. + * `samples` is the number of float values pointed to by `pcm`: samples, not + * sample frames! There are no promises how many samples will be provided + * per-callback, and this number can vary wildly from call to call, depending + * on many factors. * - * If NULL is passed, returns duration of current playing music. + * \param userdata an opaque Pointer provided by the app for its personal use. + * \param mixer the mixer that is generating audio. + * \param spec the format of the data in `pcm`. + * \param pcm the raw PCM data in float32 format. + * \param samples the number of float values pointed to by `pcm`. * - * \param music the music object to query. - * \returns -1.0 if this feature is not used for this music or not supported - * for some codec. + * \since This datatype is available since SDL_mixer 3.0.0. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetMusicLoopLengthTime(music: PMix_Music): cdouble; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetMusicLoopLengthTime' {$ENDIF} {$ENDIF}; + * \sa MIX_SetPostMixCallback + } +type + TMIX_PostMixCallback = procedure (userdata: Pointer; mixer: PMIX_Mixer; spec: PSDL_AudioSpec; pcm: pcfloat; samples: cint); cdecl; -{** - * Check the playing status of a specific channel. +{* + * Set a callback that fires when all mixing has completed. * - * If the channel is currently playing, this function returns 1. Otherwise it - * returns 0. + * After all mixer groups have processed, their buffers are mixed together + * into a single buffer for the final output, at which point a callback can be + * fired. This lets an app view the data at the last moment before mixing + * completes. It can also change the data in any way it pleases during this + * callback, and the mixer will continue as if this data is the final output. * - * If the specified channel is -1, all channels are checked, and this function - * returns the number of channels currently playing. + * Each mixer has its own unique callback. * - * You may not specify MAX_CHANNEL_POST for a channel. + * Passing a nil callback here is legal; it disables this mixer's callback. * - * Paused channels are treated as playing, even though they are not currently - * making forward progress in mixing. - * - * \param channel channel. - * \returns non-zero if channel is playing, zero otherwise. If `channel` is - * -1, return the total number of channel playings. + * \param mixer the mixer to assign this callback to. + * \param cb the function to call when the mixer mixes. May be nil. + * \param userdata an opaque Pointer provided to the callback for its own + * personal use. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_Playing(channel: cint): cint; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_Playing' {$ENDIF} {$ENDIF}; - -{** - * Check the playing status of the music stream. * - * If music is currently playing, this function returns 1. Otherwise it - * returns 0. - * - * Paused music is treated as playing, even though it is not currently making - * forward progress in mixing. + * \sa MIX_PostMixCallback + } +function MIX_SetPostMixCallback(mixer: PMIX_Mixer; cb: TMIX_PostMixCallback; userdata: Pointer): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_SetPostMixCallback' {$ENDIF} {$ENDIF}; + +{ Audio generation without an audio device... } +{* + * Generate mixer output when not driving an audio device. + * + * SDL_mixer allows the creation of MIX_Mixer objects that are not connected + * to an audio device, by calling MIX_CreateMixer() instead of + * MIX_CreateMixerDevice(). Such mixers will not generate output until + * explicitly requested through this function. + * + * The caller may request as much audio as desired, so long as `buflen` is a + * multiple of the sample frame size specified when creating the mixer (for + * example, if requesting stereo Sint16 audio, buflen must be a multiple of 4: + * 2 bytes-per-channel times 2 channels). + * + * The mixer will mix as quickly as possible; since it works in sample frames + * instead of time, it can potentially generate enormous amounts of audio in a + * small amount of time. + * + * On success, this always fills `buffer` with `buflen` bytes of audio; if all + * playing tracks finish mixing, it will fill the remaining buffer with + * silence. + * + * Each call to this function will pick up where it left off, playing tracks + * will continue to mix from the point the previous call completed, etc. The + * mixer state can be changed between each call in any way desired: tracks can + * be added, played, stopped, changed, removed, etc. Effectively this function + * does the same thing SDL_mixer does internally when the audio device needs + * more audio to play. + * + * This function can not be used with mixers from MIX_CreateMixerDevice(); + * those generate audio as needed internally. + * + * This function returns the number of _bytes_ of real audio mixed, which + * might be less than `buflen`. While all `buflen` bytes of `buffer` will be + * initialized, if available tracks to mix run out, the end of the buffer will + * be initialized with silence; this silence will not be counted in the return + * value, so the caller has the option to identify how much of the buffer has + * legimitate contents vs appended silence. As such, any value >= 0 signifies + * success. A return value of -1 means failure (out of memory, invalid + * parameters, etc). + * + * \param mixer the mixer for which to generate more audio. + * \param buffer a Pointer to a buffer to store audio in. + * \param buflen the number of bytes to store in buffer. + * \returns The number of bytes of mixed audio, discounting appended silence, + * on success, or -1 on failure; call SDL_GetError() for more + * information. * - * \returns true if music is playing, false otherwise. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_PlayingMusic(): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_PlayingMusic' {$ENDIF} {$ENDIF}; + * + * \sa MIX_CreateMixer + } +function MIX_Generate(mixer: PMIX_Mixer; buffer: Pointer; buflen: cint): cint; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_Generate' {$ENDIF} {$ENDIF}; -{** - * Set SoundFonts paths to use by supported MIDI backends. +{ Decode audio files directly without a mixer ... } +{* + * An opaque object that represents an audio decoder. * - * You may specify multiple paths in a single string by separating them with - * semicolons; they will be searched in the order listed. + * Most apps won't need this, as SDL_mixer's usual interfaces will decode + * audio as needed. However, if one wants to decode an audio file into a + * memory buffer without playing it, this interface offers that. * - * This function replaces any previously-specified paths. + * These objects are created with MIX_CreateAudioDecoder() or + * MIX_CreateAudioDecoder_IO(), and then can use MIX_DecodeAudio() to retrieve + * the raw PCM data. * - * Passing a NULL path will remove any previously-specified paths. + * \since This struct is available since SDL_mixer 3.0.0. + } +type + PPMIX_AudioDecoder = ^PMIX_AudioDecoder; + PMIX_AudioDecoder = type Pointer; + +{* + * Create a MIX_AudioDecoder from a path on the filesystem. * - * \param paths Paths on the filesystem where SoundFonts are available, - * separated by semicolons. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * Most apps won't need this, as SDL_mixer's usual interfaces will decode + * audio as needed. However, if one wants to decode an audio file into a + * memory buffer without playing it, this interface offers that. * - * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_SetSoundFonts(const paths: PAnsiChar): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_SetSoundFonts' {$ENDIF} {$ENDIF}; - -{** - * Get SoundFonts paths to use by supported MIDI backends. + * This function allows properties to be specified. This is intended to supply + * file-specific settings, such as where to find SoundFonts for a MIDI file, + * etc. In most cases, the caller should pass a zero to specify no extra + * properties. + * + * SDL_PropertiesID are discussed in + * [SDL's documentation](https://wiki.libsdl.org/SDL3/CategoryProperties) + * . * - * There are several factors that determine what will be reported by this - * function: + * When done with the audio decoder, it can be destroyed with + * MIX_DestroyAudioDecoder(). * - * - If the boolean _SDL hint_ `"SDL_FORCE_SOUNDFONTS"` is set, AND the - * `"SDL_SOUNDFONTS"` _environment variable_ is also set, this function will - * return that environment variable regardless of whether - * Mix_SetSoundFonts() was ever called. - * - Otherwise, if Mix_SetSoundFonts() was successfully called with a non-NULL - * path, this function will return the string passed to that function. - * - Otherwise, if the `"SDL_SOUNDFONTS"` variable is set, this function will - * return that environment variable. - * - Otherwise, this function will search some common locations on the - * filesystem, and if it finds a SoundFont there, it will return that. - * - Failing everything else, this function returns NULL. + * This function requires SDL_mixer to have been initialized with a successful + * call to MIX_Init(), but does not need an actual MIX_Mixer to have been + * created. * - * This returns a pointer to internal (possibly read-only) memory, and it - * should not be modified or free'd by the caller. + * \param path the path on the filesystem from which to load data. + * \param props decoder-specific properties. May be zero. + * \returns an audio decoder, ready to decode. * - * \returns semicolon-separated list of sound font paths. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetSoundFonts(): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetSoundFonts' {$ENDIF} {$ENDIF}; - -type - TMix_EachSoundFontCallback = function(const path: PAnsiChar; userdata: Pointer): Boolean; cdecl; - PMix_EachSoundFontCallback = ^TMix_EachSoundFontCallback; - PPMix_EachSoundFontCallback = ^PMix_EachSoundFontCallback; + * + * \sa MIX_CreateAudioDecoder_IO + * \sa MIX_DecodeAudio + * \sa MIX_DestroyAudioDecoder + } +function MIX_CreateAudioDecoder(path: PAnsiChar; props: TSDL_PropertiesID): PMIX_AudioDecoder; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_CreateAudioDecoder' {$ENDIF} {$ENDIF}; -{** - * Iterate SoundFonts paths to use by supported MIDI backends. +{* + * Create a MIX_AudioDecoder from an SDL_IOStream. + * + * Most apps won't need this, as SDL_mixer's usual interfaces will decode + * audio as needed. However, if one wants to decode an audio file into a + * memory buffer without playing it, this interface offers that. + * + * This function allows properties to be specified. This is intended to supply + * file-specific settings, such as where to find SoundFonts for a MIDI file, + * etc. In most cases, the caller should pass a zero to specify no extra + * properties. + * + * If `closeio` is true, then `io` will be closed when this decoder is done + * with it. If this function fails and `closeio` is true, then `io` will be + * closed before this function returns. * - * This function will take the string reported by Mix_GetSoundFonts(), split - * it up into separate paths, as delimited by semicolons in the string, and - * call a callback function for each separate path. + * When done with the audio decoder, it can be destroyed with + * MIX_DestroyAudioDecoder(). * - * If there are no paths available, this returns 0 without calling the - * callback at all. + * This function requires SDL_mixer to have been initialized with a successful + * call to MIX_Init(), but does not need an actual MIX_Mixer to have been + * created. * - * If the callback returns non-zero, this function stops iterating and returns - * non-zero. If the callback returns 0, this function will continue iterating, - * calling the callback again for further paths. If the callback never returns - * 1, this function returns 0, so this can be used to decide if an available - * soundfont is acceptable for use. + * \param io the i/o stream from which to load data. + * \param closeio if true, close the i/o stream when done with it. + * \param props decoder-specific properties. May be zero. + * \returns an audio decoder, ready to decode. * - * \param func the callback function to call once per path. - * \param data a pointer to pass to the callback for its own personal use. - * \returns true if callback ever returned true, false on error or if the - * callback never returned true. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_GetSoundFonts - *} -function Mix_EachSoundFont(func: TMix_EachSoundFontCallback; data: Pointer): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_EachSoundFont' {$ENDIF} {$ENDIF}; + * \sa MIX_CreateAudioDecoder_IO + * \sa MIX_DecodeAudio + * \sa MIX_DestroyAudioDecoder + } +function MIX_CreateAudioDecoder_IO(io: PSDL_IOStream; closeio: Boolean; props: TSDL_PropertiesID): PMIX_AudioDecoder; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_CreateAudioDecoder_IO' {$ENDIF} {$ENDIF}; -{** - * Set full path of the Timidity config file. +{* + * Destroy the specified audio decoder. * - * For example, "/etc/timidity.cfg" + * Destroying a nil MIX_AudioDecoder is a legal no-op. * - * This is obviously only useful if SDL_mixer is using Timidity internally to - * play MIDI files. + * \param audiodecoder the audio to destroy. * - * \param path path to a Timidity config file. - * \returns true on success or false on failure; call SDL_GetError() for more - * information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_SetTimidityCfg(const path: PAnsiChar): Boolean; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_SetTimidityCfg' {$ENDIF} {$ENDIF}; + } +procedure MIX_DestroyAudioDecoder(audiodecoder: PMIX_AudioDecoder); cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_DestroyAudioDecoder' {$ENDIF} {$ENDIF}; -{** - * Get full path of a previously-specified Timidity config file. +{* + * Get the properties associated with a MIX_AudioDecoder. * - * For example, "/etc/timidity.cfg" + * SDL_mixer offers some properties of its own, but this can also be a + * convenient place to store app-specific data. * - * If a path has never been specified, this returns NULL. + * A SDL_PropertiesID is created the first time this function is called for a + * given MIX_AudioDecoder, if necessary. * - * This returns a pointer to internal memory, and it should not be modified or - * free'd by the caller. + * The file-specific metadata exposed through this function is identical to + * those available through MIX_GetAudioProperties(). Please refer to that + * function's documentation for details. * - * \returns the previously-specified path, or NULL if not set. + * \param audiodecoder the audio decoder to query. + * \returns a valid property ID on success or 0 on failure; call + * SDL_GetError() for more information. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. * - * \sa Mix_SetTimidityCfg - *} -function Mix_GetTimidityCfg(): PAnsiChar; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetTimidityCfg' {$ENDIF} {$ENDIF}; + * \sa MIX_GetAudioProperties + } +function MIX_GetAudioDecoderProperties(audiodecoder: PMIX_AudioDecoder): TSDL_PropertiesID; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetAudioDecoderProperties' {$ENDIF} {$ENDIF}; -{** - * Get the Mix_Chunk currently associated with a mixer channel. +{* + * Query the initial audio format of a MIX_AudioDecoder. + * + * Note that some audio files can change format in the middle; some explicitly + * support this, but a more common example is two MP3 files concatenated + * together. In many cases, SDL_mixer will correctly handle these sort of + * files, but this function will only report the initial format a file uses. * - * You may not specify MAX_CHANNEL_POST or -1 for a channel. + * \param audiodecoder the audio decoder to query. + * \param spec on success, audio format details will be stored here. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. * - * \param channel the channel to query. - * \returns the associated chunk, if any, or NULL if it's an invalid channel. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - *} -function Mix_GetChunk(channel: cint): PMix_Chunk; cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_GetChunk' {$ENDIF} {$ENDIF}; + } +function MIX_GetAudioDecoderFormat(audiodecoder: PMIX_AudioDecoder; spec: PSDL_AudioSpec): Boolean; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_GetAudioDecoderFormat' {$ENDIF} {$ENDIF}; -{** - * Close the mixer, halting all playing audio. - * - * Any halted channels will have any currently-registered effects - * deregistered, and will call any callback specified by Mix_ChannelFinished() - * before this function returns. - * - * Any halted music will call any callback specified by - * Mix_HookMusicFinished() before this function returns. +{* + * Decode more audio from a MIX_AudioDecoder. * - * Do not start any new audio playing during callbacks in this function. + * Data is decoded on demand in whatever format is requested. The format is + * permitted to change between calls. * - * This will close the audio device. Attempting to play new audio after this - * function returns will fail, until another successful call to - * Mix_OpenAudio(). + * This function will return the number of bytes decoded, which may be less + * than requested if there was an error or end-of-file. A return value of zero + * means the entire file was decoded, -1 means an unrecoverable error + * happened. * - * Note that (unlike Mix_OpenAudio optionally calling SDL_Init(SDL_INIT_AUDIO) - * on the app's behalf), this will _not_ deinitialize the SDL audio subsystem - * in any case. At some point after calling this function and Mix_Quit(), some - * part of the application should be responsible for calling SDL_Quit() to - * deinitialize all of SDL, including its audio subsystem. + * \param audiodecoder the decoder from which to retrieve more data. + * \param buffer the memory buffer to store decoded audio. + * \param buflen the maximum number of bytes to store to `buffer`. + * \param spec the format that audio data will be stored to `buffer`. + * \returns number of bytes decoded, or -1 on error; call SDL_GetError() for + * more information. * - * This function should be the last thing you call in SDL_mixer before - * Mix_Quit(). However, the following notes apply if you don't follow this - * advice: - * - * Note that this will not free any loaded chunks or music; you should dispose - * of those resources separately. It is probably poor form to dispose of them - * _after_ this function, but it is safe to call Mix_FreeChunk() and - * Mix_FreeMusic() after closing the device. - * - * Note that any chunks or music you don't free may or may not work if you - * call Mix_OpenAudio again, as the audio device may be in a new format and - * the existing chunks will not be converted to match. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL_mixer 3.0.0. - * - * \sa Mix_Quit - *} -procedure Mix_CloseAudio(); cdecl; - external MIX_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_Mix_CloseAudio' {$ENDIF} {$ENDIF}; + } +function MIX_DecodeAudio(audiodecoder: PMIX_AudioDecoder; buffer: Pointer; buflen: cint; spec: PSDL_AudioSpec): cint; cdecl; + external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_MIX_DecodeAudio' {$ENDIF} {$ENDIF}; -Implementation +implementation -Function SDL_MIXER_VERSION(): Integer; -Begin +function SDL_MIXER_VERSION(): Integer; +begin Result := SDL_VERSIONNUM(SDL_MIXER_MAJOR_VERSION, SDL_MIXER_MINOR_VERSION, SDL_MIXER_MICRO_VERSION) -End; +end; -Function SDL_MIXER_VERSION_ATLEAST(major, minor, micro: Integer): Boolean; -Begin +function SDL_MIXER_VERSION_ATLEAST(major, minor, micro: Integer): Boolean; +begin Result := (SDL_MIXER_MAJOR_VERSION >= major) and ((SDL_MIXER_MAJOR_VERSION > major) or (SDL_MIXER_MINOR_VERSION >= minor)) and ((SDL_MIXER_MAJOR_VERSION > major) or (SDL_MIXER_MINOR_VERSION > minor) or (SDL_MIXER_MICRO_VERSION >= micro)) -End; +end; end.