Skip to content

The SDL_Mixer API Redesign Megathread #662

Open
@icculus

Description

@icculus

Here's my first pitch at a redesigned SDL_mixer API. I took the same approach as SDL_net, where all of it goes in the trash and we start from scratch.

The idea is this:

  • You no longer have a separation between "chunks" and "music".
  • Channels are replaced by "Mix_Source" objects. They are dynamic, you don't have a set block of channels.
  • You load sound data into "Mix_Audio" objects, which can be used on multiple Mix_Sources at once.
  • Most of the functionality of the existing library is still here, possibly in a better form. Gone is the "effects" API, because all of that code sucked, and was generally limited to stereo audio anyhow. In its place, there are hooks to edit audio as it is about to be mixed, where any effect could be implemented, and the ability to wire up an SDL_AudioStream to a Mix_Source, so you can put any dynamic audio into the mixer that you want, as just another source to be mixed. The post-mix callback remains, too, which can be used for what the current library calls "posteffects."

This API spec is extremely terse, so don't be afraid to ask questions. Obviously nothing is locked down at this point, so feedback is definitely welcome.

EDIT: This initial proposal is out of date, so I've hidden it until you click through.
// there is no separate "init" function. You open the audio device (presumably just the default audio device) and go.

bool Mix_OpenAudio(SDL_AudioDeviceID devid);  // this will call SDL_Init(SDL_INIT_AUDIO), open audio device.
void Mix_CloseAudio(void);  // this will call SDL_QuitSubSystem(SDL_INIT_AUDIO).


int Mix_GetNumAudioDecoders(void);
const char *Mix_GetAudioDecoder(int index);  // "WAV", "MP3", etc.

bool Mix_QuerySpec(SDL_AudioSpec *spec);   // what the device is actually expecting.

// there is no difference between sounds and "music" now. They're all Mix_Audio objects.
Mix_Audio *Mix_LoadAudio_IO(SDL_IOStream *src, bool closeio);
Mix_Audio *Mix_LoadAudio(const char *path);
Mix_Audio *Mix_LoadAudioWithProperties(SDL_PropertiesID props);  // lets you specify things like "here's a path to MIDI instrument files outside of this file", etc.

SDL_PropertiesID Mix_GetAudioProperties(Mix_Audio *audio);  // we can store audio format-specific metadata in here (artist/album/etc info...)

void Mix_DestroyAudio(Mix_Audio *audio);  // reference-counted; if this is playing, it will be _actually_ destroyed when no longer in use.


// Sources are your "channels" but they aren't static anymore. Just make as
// many as you like and destroy them as you like. If you want the old
// semantics, just make as many as you would have allocated "channels" and put
// them in an array somewhere.

Mix_Source *Mix_CreateSource(void);
void Mix_DestroySource(Mix_Source *src);  // will halt playback, if playing. Won't call Finished callback, though. We assume you know.

bool Mix_SetSourceAudio(Mix_Source *src, Mix_Audio *audio);  // Source will replace current audio with new one. If currently playing, will start playing new audio immediately.
bool Mix_SetSourceAudioStream(Mix_Source *src, SDL_AudioStream *stream);  // insert anything you like into the mix. procedural audio, VoIP, data right from a microphone, etc. Will pull from AudioStream as needed instead of a Mix_Audio.

bool Mix_TagSource(Mix_Source *src, const char *tag);  // add an arbitrary tag to a Mix_Source. You can group audio this way. A Mix_Source can have multiple tags.
void Mix_UntagSource(Mix_Source *src, const char *tag);  // remove an arbitrary tag from a Mix_Source.

bool Mix_SetSourcePlaybackPosition(Mix_Source *src, Uint64 position);  // set source playback position to X milliseconds in. Must be fed from a Mix_Audio that can seek, other limitations.
Uint64 Mix_GetSourcePlaybackPosition(Mix_Source *audio);  // milliseconds of audio that have been played from the start of this Mix_Source.


// operations that deal with actual mixing/playback...

// play a Mix_Source.
// if (fireAndForget) the Mix_Source is destroyed if it halts for any reason.
// if (maxTicks >= 0), it halts/loops after X milliseconds of playback.
// if (loops >= 0), it loops this many times then halts (so 0==play once, 1==play twice). if < 0, loop forever.
// if (fadeIn > 0), it fades in from silence over X milliseconds. If looping, only first iteration fades in.
bool Mix_PlaySource(Mix_Source *src, bool fireAndForget, Sint64 maxTicks, int loops, Sint64 fadeIn);
bool Mix_PlayTag(const char *tag, bool fireAndForget, Sint64 maxTicks, int loops, Sint64 fadeIn);  // play everything with this tag.

// halt playing audio. If (fadeOut > 0), fade out over X milliseconds before halting. if <= 0, halt immediately.
bool Mix_HaltSource(Mix_Source *src, Sint64 fadeOut);  // halt a playing Mix_Source. No-op if not playing.
bool Mix_HaltAllSources(Sint64 fadeOut);  // halt anything that's playing.
bool Mix_HaltTag(const char *tag, Sint64 fadeOut);  // halt all playing Mix_Sources with a matching tag.

// Pausing is not halting (so no finished callback, fire-and-forget sources don't destruct, resuming doesn't rewind audio to start).
bool Mix_PauseSource(Mix_Source *src);  // pause a playing Mix_Source. No-op if not playing.
bool Mix_PauseAllSources(Sint64 fadeOut);  // pause anything that's playing.
bool Mix_PauseTag(const char *tag, Sint64 fadeOut);  // pause all playing Mix_Sources with a matching tag.

// Resuming is the opposite of pausing. You can't resume a source that isn't paused.
bool Mix_ResumeSource(Mix_Source *src);  // resume a playing Mix_Source. No-op if not paused.
bool Mix_ResumeAllSources(Sint64 fadeOut);  // resume anything that's playing.
bool Mix_ResumeTag(const char *tag, Sint64 fadeOut);  // resume all playing Mix_Sources with a matching tag.

bool Mix_Playing(Mix_Source *src);  // true if source is playing.
bool Mix_Paused(Mix_Source *src);  // true if source is paused.

bool Mix_SetFinishedCallback(Mix_Source *src, Mix_SourceFinishedCallback cb, void *userdata);  // if set, is called when a src halts for any reason except destruction.


// volume control...

void Mix_SetMasterGain(float gain);  // one knob that adjusts all playing sounds. Modulates with per-Mix_Source gain.
float Mix_GetMasterGain(void);

void Mix_SetGain(Mix_Source *src, float gain);  // Change gain for this one Mix_Source.
void Mix_GetGain(Mix_Source *src, float gain);
void Mix_SetTagGain(const char *tag, float gain);  // Change gain for all Mix_Sources with this tag.


// hooks...

void Mix_SetPostMix(SDL_AudioPostmixCallback mix_func, void *userdata);  // just calls the standard SDL postmix callback.
void Mix_SetSourceMix(Mix_Source *src, Mix_SourceMixCallback cb, void *userdata);  // is called as data is to be mixed, so you can view (and edit) the source's data. Always in float32 format!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions