414 lines
17 KiB
Markdown
414 lines
17 KiB
Markdown
# Migrating to SDL_mixer 3.0
|
|
|
|
## Intro
|
|
|
|
SDL_mixer 3.0 (aka "SDL3_mixer") is a dramatically different library than
|
|
previous versions. The API has been completely redesigned. There is no
|
|
compatibility layer. If you want to use it, you have to migrate to it.
|
|
|
|
SDL3_mixer requires SDL3. It relies on many features that are new to SDL3,
|
|
both internally and in the public API, so if your project is on SDL 1.2 or
|
|
SDL2, you'll have to move your project to SDL3 at the same time.
|
|
|
|
That being said, we think SDL3_mixer and SDL3 are both great pieces of
|
|
software--significant improvements over their previous versions--and we
|
|
think that once you move to them, you'll be quite happy you did.
|
|
|
|
There are a lot of things that don't have simple replacements that can be
|
|
changed mechanically to migrate to SDL3_mixer. The new API is in many ways
|
|
more powerful, but also much simpler. For example, there's no equivalent of
|
|
Mix_ModMusicJumpToOrder(), because messing with the specifics
|
|
of MOD files in the public API is both uncommon and generally pretty messy.
|
|
|
|
This migration guide will attempt to walk through the important details but
|
|
it's possible that some things can't be done the same way. Feel free to open
|
|
bug reports if you're totally stuck.
|
|
|
|
The end of this document provides a "tl;dr" section that lists each SDL2_mixer
|
|
function name and a brief explanation about what to do with it.
|
|
|
|
|
|
## Things that are totally gone
|
|
|
|
- Native MIDI has been removed. We have ripped this piece out of SDL2_mixer
|
|
and packaged it as a separate library that can be used alongside SDL3_mixer,
|
|
or without SDL_mixer at all: https://github.com/libsdl-org/SDL_native_midi
|
|
|
|
- Mix_GetNumTracks(), Mix_StartTrack(), and Mix_ModMusicJumpToOrder() have
|
|
been removed; these were decoder-specific APIs.
|
|
|
|
- Mix_SetSoundFonts(), Mix_GetSoundFonts(), Mix_EachSoundFont(),
|
|
Mix_SetTimidityCfg(), Mix_GetTimidityCfg(): these have been removed, but
|
|
decoder-specific settings can be passed on in a generic way in SDL3_mixer,
|
|
using SDL properties.
|
|
|
|
- Mix_QuickLoad_WAV(): this was just too unsafe to offer.
|
|
|
|
|
|
## Including SDL3_mixer
|
|
|
|
The proper way to include SDL3_mixer's header is:
|
|
|
|
```c
|
|
#include <SDL3_mixer/SDL_mixer.h>
|
|
```
|
|
|
|
Like SDL3, the new convention is to use `<>` brackets and a subdirectory.
|
|
|
|
|
|
## Symbol names
|
|
|
|
In SDL2_mixer, all functions started with `Mix_` and macros started with
|
|
`MIX_`. In SDL3_mixer, everything starts with `MIX_`.
|
|
|
|
|
|
## Versioning
|
|
|
|
Versions are now bits packed into a single int instead of a struct, using
|
|
SDL3's usual magic for unpacking these.
|
|
|
|
|
|
## Initialization
|
|
|
|
Previously you had to tell SDL_mixer what file formats you expected to use
|
|
during Mix_Init(). Now you don't:
|
|
|
|
```c
|
|
if (!MIX_Init()) {
|
|
SDL_Log("MIX_Init failed: %s", SDL_GetError());
|
|
} else {
|
|
SDL_Log("SDL_mixer is ready!");
|
|
}
|
|
```
|
|
|
|
MIX_Init() is reference-counted; it's safe to call it more than once, and
|
|
only the first call will do actual initialization tasks. Likewise, actual
|
|
deinitialization with MIX_Quit() will only happen when it has been called
|
|
as many times as MIX_Init() has. This allows different parts of the program
|
|
to initialize and use SDL3_mixer without knowing about other parts using it
|
|
too.
|
|
|
|
Previously the (singleton) mixer was opened with Mix_OpenAudio(), now one
|
|
calls MIX_CreateMixerDevice(). You can open multiple mixers now, in case
|
|
different parts of a program want to operate independently, or you need to
|
|
use different audio devices at the same time (or, with Mix_CreateMixer(),
|
|
write audio to a memory buffer instead of a device).
|
|
|
|
MIX_CreateMixerDevice() accepts an SDL_AudioSpec as a hint, but makes no
|
|
promises the audio device will be at any given format. The hint is just
|
|
to indicate that most of the inputs will be in a certain format, and maybe
|
|
this will reduce conversion work if the device can handle it. But SDL3_mixer
|
|
is well-prepared to manage any input or output formats it ends up with.
|
|
|
|
Mix_QuerySpec() has been replaced by MIX_GetMixerFormat().
|
|
|
|
|
|
## Locking
|
|
|
|
SDL2_mixer expected you to lock the SDL audio device directly if you needed
|
|
explicit synchronization. SDL3 doesn't have a direct equivalent of SDL2's
|
|
SDL_LockAudioDevice(), and SDL3_mixer might be operating without an audio
|
|
device at all, so there are now explicit MIX_LockMixer() and MIX_UnlockMixer()
|
|
API calls available.
|
|
|
|
|
|
## Music vs chunks
|
|
|
|
SDL2_mixer made a distinction between "chunks" (uncompressed audio sitting in
|
|
memory, to be played on a "channel") and "music" (streaming, probably
|
|
compressed, audio). It offered multiple chunks to be mixed together, along
|
|
with a single music input.
|
|
|
|
In SDL3_mixer, there is no distinction between types of audio; all inputs can
|
|
be compressed or uncompressed, preloaded into memory or streamed, in any
|
|
supported file format. As many as you like of any type can mix at once.
|
|
|
|
Preloaded (but not necessarily predecoded) audio is managed in MIX_Audio
|
|
objects. These are audio files precached in RAM. They can be decompressed
|
|
upfront, or on the fly while mixing.
|
|
|
|
In SDL2_mixer, data would be loaded with Mix_LoadWAV() for chunks, or
|
|
Mix_LoadMUS() for music, or some variant thereof. In SDL3_mixer, you will call
|
|
MIX_LoadAudio(), MIX_LoadAudio_IO(), or if you have some unconventional need,
|
|
MIX_LoadAudioWithProperties().
|
|
|
|
A special function in SDL2_mixer, Mix_QuickLoad_RAW(), has been replaced with
|
|
MIX_LoadRawAudioNoCopy(). It's a little more generic (it does not require your
|
|
data be in the same format as the hardware), but it will still let you get
|
|
data all the way through the mixing pipeline without any extra allocations or
|
|
copies.
|
|
|
|
Many functions in SDL2_mixer had two versions: one for chunks and one for
|
|
music, like Mix_FreeChunk() vs Mix_FreeMusic(). In SDL3_mixer, equivalent
|
|
APIs have a single function that works with everything.
|
|
|
|
Mix_GetNumChunkDecoders() and Mix_GetChunkDecoder() (and the Music
|
|
equivalents) have been replaced with MIX_GetNumAudioDecoders() and
|
|
MIX_GetAudioDecoder(). The returned strings might be different than in
|
|
SDL2_mixer for the same decoding backends.
|
|
|
|
|
|
## Channels
|
|
|
|
SDL2_mixer had "channels," each of which contained a single chunk to be mixed
|
|
during playback. Channels were referenced by index, and you would preallocate
|
|
the number of channels you intended to use with Mix_AllocateChannels() (unless
|
|
you needed 8 channels, the default, in which case you just used them).
|
|
|
|
SDL3_mixer has "tracks," each containing a single input (a loaded audio file,
|
|
an SDL_AudioStream for dynamically generated sound, or an SDL_IOStream to
|
|
stream a file on-demand). They are allocated one at a time, and referenced by
|
|
the pointer returned from MIX_CreateTrack(). To keep an indexable set of
|
|
tracks to match SDL2_mixer's channels, just call MIX_CreateTrack() in a loop,
|
|
storing the pointers in an array, and use indices into that array. There are
|
|
no tracks created by default, so to match SDL2_mixer's default, create 8 of
|
|
them at startup.
|
|
|
|
|
|
## Metadata
|
|
|
|
Functions that return metadata about an audio file, like Mix_GetMusicTitle(),
|
|
have been removed. Instead, all audio files have a set of SDL properties.
|
|
|
|
These properties offer both low-level tag information and a standard property
|
|
for well-known metadata types. For example, if your MP3 has an ID3v2 tag, you
|
|
might end up with a property named "SDL_mixer.metadata.id3v2.TIT2" and the
|
|
same info in a standard "SDL_mixer.metadata.title" property (which there's a
|
|
standard symbol for: `MIX_PROP_METADATA_TITLE_STRING`. This allows SDL3_mixer
|
|
to expose non-standard information, but also offer easy lookup of the most
|
|
common, and most important, metadata.
|
|
|
|
After loading an audio file, MIX_GetAudioProperties() will provide the
|
|
metadata (and also provide a convenient place for the app to hang their own
|
|
app-specific data, as well).
|
|
|
|
|
|
## Hooks
|
|
|
|
SDL3_mixer offers several callbacks at different points in the mixing
|
|
pipeline. They can be used to recreate SDL2_mixer's Mix_SetPostMix(),
|
|
Mix_HookMusic(), Mix_HookMusicFinished(), Mix_ChannelFinished(), and
|
|
Mix_RegisterEffect().
|
|
|
|
Note that in SDL3_mixer, all callbacks provide audio data in float32 format,
|
|
so you won't need to have different paths for different formats.
|
|
|
|
|
|
## Effects
|
|
|
|
SDL2_mixer had several "effects" that were internal code that used the
|
|
Mix_RegisterEffect() interface to modify chunks before they were mixed.
|
|
|
|
Mix_SetPanning() can be replaced with MIX_SetTrackStereo(), but this will
|
|
force the track to play on the Front Left and Front Right speakers in a
|
|
surround-sound setup. If you actually want the track to move left to
|
|
right, respecting any surround-sound layout, use MIX_SetTrack3DPosition()
|
|
and move the sound across the X axis.
|
|
|
|
Mix_SetPosition() and Mix_SetDistance() can also use MIX_SetTrack3DPosition()
|
|
to better effect, but you'll have to convert to 3D coordinates instead of
|
|
angle/distance. Here's some (untested) math to do that:
|
|
|
|
```c
|
|
const float radians = angle * (SDL_PI_F / 180.0f); // convert degrees to radians.
|
|
const float fdistance = ((float) distance);
|
|
const float x = SDL_cosf(radians) * fdistance; // left to right
|
|
const float y = 0.0f; // leave everything vertically centered on the listener.
|
|
const float z = SDL_sinf(radians) * fdistance; // front to back
|
|
```
|
|
|
|
Mix_SetReverseStereo() can be replaced with MIX_SetTrackOutputChannelMap():
|
|
|
|
```c
|
|
const int chmap = { 1, 0 }; // left becomes right, right becomes left.
|
|
MIX_SetTrackOutputChannelMap(track, chmap, 2);
|
|
```
|
|
|
|
|
|
## Channel groups
|
|
|
|
In SDL2_mixer, you could "tag" channels with an int value, and then reference
|
|
all channels that match that tag.
|
|
|
|
In SDL3_mixer, you can still tag a track, but the tag is an arbitrary string,
|
|
like "ui" or "ambient" or whatever. A track can have multiple tags.
|
|
|
|
Tag tracks with MIX_TagTrack() and remove a tag with MIX_UntagTrack().
|
|
|
|
|
|
## Playing
|
|
|
|
There are several different functions in SDL2_mixer to start a channel playing
|
|
(Mix_Play*, Mix_FadeIn*, etc). All of these have collapsed into MIX_PlayTrack().
|
|
MIX_PlayTrack() takes an SDL_PropertiesID for options (if the defaults don't cover
|
|
it). Before playing, a track must have an input assigned, either through
|
|
MIX_SetTrackAudio() to play from preloaded audio, MIX_SetTrackIOStream() to play
|
|
from a file loaded on-demand, or MIX_SetTrackAudioStream() to play procedural
|
|
audio, either buffered upfront or generated on-demand.
|
|
|
|
|
|
## Volume
|
|
|
|
Mix_VolumeChunk() and Mix_VolumeMusic() are replaced by MIX_SetTrackGain(). They
|
|
take a float instead of an int from 0 to 128, and can be used to not only quiet
|
|
the existing data, but also make it louder.
|
|
|
|
Mix_MasterVolume() is replaced by MIX_SetMixerGain(). Same idea.
|
|
|
|
|
|
## Halting
|
|
|
|
Mix_HaltChannel(), MIX_HaltMusic(), Mix_FadeOutChannel() and Mix_FadeOutMusic()
|
|
are replaced by MIX_StopTrack(). Be careful here: fade-outs work in sample
|
|
frames of the track's input, not milliseconds, in SDL3_mixer. Use
|
|
MIX_TrackMSToFrames() to convert. However, If stopping multiple tracks at once
|
|
with MIX_StopAllTracks() or MIX_StopTag(), the fade out is specified in
|
|
_milliseconds_, since we can't guarantee that all tracks have the same sample
|
|
rate.
|
|
|
|
|
|
## Pausing
|
|
|
|
Mix_Pause(), Mix_PauseMusic(), Mix_Resume() and Mix_ResumeMusic() became
|
|
MIX_PauseTrack() and MIX_ResumeTrack().
|
|
|
|
|
|
## Music state
|
|
|
|
Mix_MusicDuration() became MIX_GetAudioDuration() and Mix_GetMusicPosition() became
|
|
MIX_GetTrackPlaybackPosition(). MIX_SetMusicPosition() is now
|
|
MIX_SetTrackPlaybackPosition().
|
|
|
|
|
|
## Shutdown
|
|
|
|
Mix_CloseAudio() is now MIX_DestroyMixer(). Destroying a mixer will destroy
|
|
everything it owns (tracks, etc), but not MIX_Audio objects, since they can be
|
|
shared between mixers. MIX_Quit() can be used to destroy _everything_ that
|
|
SDL3_mixer created, including the mixers and MIX_Audio objects, which might be
|
|
more convenient.
|
|
|
|
|
|
## tl;dr
|
|
|
|
A very brief comment on what to do with each symbol in SDL2_mixer.
|
|
|
|
Some of these are listed as "no equivalent in SDL3_mixer" but could possibly
|
|
be added if there is a need. If you're stuck, please file an issue and we
|
|
can discuss it!
|
|
|
|
- MIX_MAJOR_VERSION => SDL_MIXER_MAJOR_VERSION
|
|
- MIX_MINOR_VERSION => SDL_MIXER_MAJOR_VERSION
|
|
- MIX_PATCHLEVEL => SDL_MIXER_MICRO_VERSION
|
|
- MIX_VERSION => SDL_MIXER_VERSION
|
|
- MIX_Linked_Version => MIX_Version
|
|
- Mix_Version => MIX_Version
|
|
- SDL_MIXER_COMPILEDVERSION => SDL_MIXER_VERSION
|
|
- MIX_InitFlags => not needed in SDL3_mixer.
|
|
- Mix_Init => MIX_Init (no initflags needed)
|
|
- Mix_Quit => MIX_Quit
|
|
- MIX_CHANNELS => not needed in SDL3_mixer.
|
|
- MIX_DEFAULT_FREQUENCY => 44100
|
|
- MIX_DEFAULT_FORMAT => SDL_AUDIO_S16
|
|
- MIX_DEFAULT_CHANNELS => 2
|
|
- MIX_MAX_VOLUME => 1.0f
|
|
- Mix_Chunk => MIX_Audio
|
|
- Mix_Fading => not needed in SDL3_mixer.
|
|
- Mix_MusicType => MIX_PROP_AUDIO_DECODER_STRING property with MIX_GetAudioProperties().
|
|
- Mix_Music => MIX_Audio
|
|
- Mix_OpenAudio => MIX_CreateMixerDevice
|
|
- Mix_PauseAudio => MIX_PauseAllTracks
|
|
- Mix_QuerySpec => MIX_GetMixerFormat
|
|
- Mix_AllocateChannels => MIX_CreateTrack
|
|
- Mix_LoadWAV_IO => MIX_LoadAudio_IO
|
|
- Mix_LoadWAV => MIX_LoadAudio
|
|
- Mix_LoadMUS_IO => MIX_LoadAudio_IO
|
|
- Mix_LoadMUS => MIX_LoadAudio
|
|
- Mix_LoadMUSType_IO => MIX_LoadAudioWithProperties with MIX_PROP_AUDIO_DECODER_STRING property set.
|
|
- Mix_QuickLoad_WAV => MIX_LoadAudio_IO with an SDL_IOFromConstMem() and closeio=true.
|
|
- Mix_QuickLoad_RAW => MIX_LoadRawAudioNoCopy
|
|
- Mix_FreeChunk => MIX_DestroyAudio
|
|
- Mix_FreeMusic => MIX_DestroyAudio
|
|
- Mix_GetNumChunkDecoders => MIX_GetNumAudioDecoders
|
|
- Mix_GetChunkDecoder => MIX_GetAudioDecoder
|
|
- Mix_HasChunkDecoder => call MIX_GetAudioDecoder, up to MIX_GetNumAudioDecoders times
|
|
- Mix_GetNumMusicDecoders => MIX_GetNumAudioDecoders
|
|
- Mix_GetMusicDecoder => MIX_GetAudioDecoder
|
|
- Mix_HasMusicDecoder => call MIX_GetAudioDecoder, up to MIX_GetNumAudioDecoders times
|
|
- Mix_MusicType => MIX_PROP_AUDIO_DECODER_STRING property with MIX_GetAudioProperties().
|
|
- Mix_GetMusicTitle => MIX_PROP_METADATA_TITLE_STRING property with Mix_GetAudioProperties()
|
|
- Mix_GetMusicTitleTag => MIX_PROP_METADATA_TITLE_STRING property with Mix_GetAudioProperties()
|
|
- Mix_GetMusicArtistTag => MIX_PROP_METADATA_ARTIST_STRING property with Mix_GetAudioProperties()
|
|
- Mix_GetMusicAlbumTag => MIX_PROP_METADATA_ALBUM_STRING property with Mix_GetAudioProperties()
|
|
- Mix_GetMusicCopyrightTag => MIX_PROP_METADATA_COPYRIGHT_STRING property with Mix_GetAudioProperties()
|
|
- Mix_SetPostMix => MIX_SetPostMixCallback
|
|
- Mix_HookMusic => MIX_SetTrackAudioStream and set a get-callback on that stream, start track playing.
|
|
- Mix_HookMusicFinished => MIX_SetTrackStoppedCallback
|
|
- Mix_GetMusicHookData => no equivalent in SDL3_mixer.
|
|
- Mix_ChannelFinished => MIX_SetTrackStoppedCallback
|
|
- MIX_CHANNEL_POST => MIX_SetPostMixCallback
|
|
- Mix_RegisterEffect => MIX_SetTrackCookedCallback, plus optional MIX_SetTrackStoppedCallback
|
|
- Mix_UnregisterEffect => MIX_SetTrackCookedCallback, plus optional MIX_SetTrackStoppedCallback
|
|
- Mix_UnregisterAllEffects => MIX_SetTrackCookedCallback, plus optional MIX_SetTrackStoppedCallback
|
|
- MIX_EFFECTSMAXSPEED => no equivalent in SDL3_mixer.
|
|
- Mix_SetPanning => MIX_SetTrackStereo or MIX_SetTrack3DPosition
|
|
- Mix_SetPosition => MIX_SetTrack3DPosition
|
|
- Mix_SetDistance => MIX_SetTrack3DPosition
|
|
- Mix_SetReverseStereo => MIX_SetTrackOutputChannelMap
|
|
- Mix_ReserveChannels => no equivalent in SDL3_mixer.
|
|
- Mix_GroupChannel => MIX_TagTrack
|
|
- Mix_GroupChannels => MIX_TagTrack
|
|
- Mix_GroupAvailable => no equivalent in SDL3_mixer.
|
|
- Mix_GroupCount => no equivalent in SDL3_mixer.
|
|
- Mix_GroupOldest => no equivalent in SDL3_mixer.
|
|
- Mix_GroupNewer => no equivalent in SDL3_mixer.
|
|
- Mix_PlayChannel => MIX_SetTrackAudio then MIX_PlayTrack
|
|
- Mix_PlayChannelTimed => MIX_SetTrackAudio then MIX_PlayTrack
|
|
- Mix_PlayMusic => MIX_SetTrackAudio then MIX_PlayTrack
|
|
- Mix_FadeInMusic => MIX_SetTrackAudio then MIX_PlayTrack
|
|
- Mix_FadeInMusicPos => MIX_SetTrackAudio then MIX_PlayTrack
|
|
- Mix_FadeInChannel => MIX_SetTrackAudio then MIX_PlayTrack
|
|
- Mix_FadeInChannelTimed => MIX_SetTrackAudio then MIX_PlayTrack
|
|
- Mix_Volume => MIX_SetTrackGain
|
|
- Mix_VolumeChunk => MIX_SetTrackGain; gain is not set on MIX_Audio, only on MIX_Track
|
|
- Mix_VolumeMusic => MIX_SetTrackGain
|
|
- Mix_GetMusicVolume => MIX_GetTrackGain
|
|
- Mix_MasterVolume => MIX_SetMixerGain
|
|
- Mix_HaltChannel => MIX_StopTrack
|
|
- Mix_HaltGroup => MIX_StopTag
|
|
- Mix_HaltMusic => MIX_StopTrack
|
|
- Mix_ExpireChannel => no equivalent in SDL3_mixer.
|
|
- Mix_FadeOutChannel => MIX_StopTrack
|
|
- Mix_FadeOutGroup => MIX_StopTag
|
|
- Mix_FadeOutMusic => MIX_StopTrack
|
|
- Mix_FadingMusic => no equivalent in SDL3_mixer.
|
|
- Mix_FadingChannel => no equivalent in SDL3_mixer.
|
|
- Mix_Pause => MIX_PauseTrack
|
|
- Mix_PauseGroup => MIX_PauseTag
|
|
- Mix_Resume => MIX_ResumeTrack
|
|
- Mix_PauseGroup => MIX_ResumeTag
|
|
- Mix_Paused => MIX_TrackPaused
|
|
- Mix_PauseMusic => MIX_PauseTrack
|
|
- Mix_ResumeMusic => MIX_ResumeTrack
|
|
- Mix_RewindMusic => MIX_SetTrackPlaybackPosition(track, 0)
|
|
- Mix_PausedMusic => MIX_TrackPaused
|
|
- Mix_ModMusicJumpToOrder => no equivalent in SDL3_mixer.
|
|
- Mix_StartTrack => no equivalent in SDL3_mixer.
|
|
- Mix_GetNumTracks => no equivalent in SDL3_mixer.
|
|
- Mix_SetMusicPosition => MIX_SetTrackPlaybackPosition
|
|
- Mix_GetMusicPosition => MIX_GetTrackPlaybackPosition
|
|
- Mix_MusicDuration => MIX_GetAudioDuration
|
|
- Mix_GetMusicLoopStartTime => no equivalent in SDL3_mixer.
|
|
- Mix_GetMusicLoopEndTime => no equivalent in SDL3_mixer.
|
|
- Mix_GetMusicLoopLengthTime => no equivalent in SDL3_mixer.
|
|
- Mix_Playing => MIX_TrackPlaying
|
|
- Mix_PlayingMusic => MIX_TrackPlaying
|
|
- Mix_SetSoundFonts => MIX_LoadAudioWithProperties with "SDL_mixer.decoder.fluidsynth.soundfont_iostream" or "SDL_mixer.decoder.fluidsynth.soundfont_path" property set
|
|
- Mix_GetSoundFonts => no equivalent in SDL3_mixer.
|
|
- Mix_EachSoundFont => no equivalent in SDL3_mixer.
|
|
- Mix_SetTimidityCfg => FIXME currently SDL_setenv("TIMIDITY_CFG")
|
|
- Mix_GetTimidityCfg => no equivalent in SDL3_mixer.
|
|
- Mix_GetChunk => MIX_GetTrackAudio or MIX_GetTrackAudioStream
|
|
- Mix_CloseAudio => MIX_DestroyMixer
|
|
|