Skip to content
This repository was archived by the owner on Jul 23, 2025. It is now read-only.

Commit 76ec5d3

Browse files
committed
Bunch More Work. Actually sort of usable now in limited ways!
Actually compiles, bunch of bug fixes etc, has a (very bad) WAV decoder, a simple test app, etc. Still a lot to go, but it's looping a sound with a custom mid-data loop point and explicit stop point that it fades in over time. It's progress!
1 parent 0a15085 commit 76ec5d3

File tree

8 files changed

+373
-62
lines changed

8 files changed

+373
-62
lines changed

include/SDL3_mixer/SDL_mixer.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ extern SDL_DECLSPEC Mix_Audio * SDLCALL Mix_LoadAudio_IO(SDL_IOStream *io, bool
5959
extern SDL_DECLSPEC Mix_Audio * SDLCALL Mix_LoadAudio(const char *path, bool predecode);
6060
extern SDL_DECLSPEC Mix_Audio * SDLCALL Mix_LoadAudioWithProperties(SDL_PropertiesID props); // lets you specify things like "here's a path to MIDI instrument files outside of this file", etc.
6161

62-
#define MIX_PROP_AUDIO_LOAD_IOSTREAM_POINTER "SDL_mixer.audio_load.iostream"
63-
#define MIX_PROP_AUDIO_LOAD_CLOSEIO_BOOLEAN "SDL_mixer.audio_load.closeio"
64-
#define MIX_PROP_AUDIO_LOAD_PREDECODE_BOOLEAN "SDL_mixer.audio_load.predecode"
62+
#define MIX_PROP_AUDIO_LOAD_IOSTREAM_POINTER "SDL_mixer.audio.load.iostream"
63+
#define MIX_PROP_AUDIO_LOAD_CLOSEIO_BOOLEAN "SDL_mixer.audio.load.closeio"
64+
#define MIX_PROP_AUDIO_LOAD_PREDECODE_BOOLEAN "SDL_mixer.audio.load.predecode"
65+
#define MIX_PROP_AUDIO_DECODER_STRING "SDL_mixer.audio.decoder"
6566

6667

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

src/SDL_mixer.c

Lines changed: 75 additions & 50 deletions
Large diffs are not rendered by default.

src/SDL_mixer_internal.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ typedef struct Mix_Decoder
2525
{
2626
const char *name;
2727
bool (SDLCALL *init)(void); // initialize the decoder (load external libraries, etc).
28-
bool (SDLCALL *prepare_audio)(const void *data, size_t datalen, SDL_AudioSpec *spec, SDL_PropertiesID props, SDL_PropertiesID *metadata_props); // see if it's a supported format, init spec, set metadata in props.
29-
bool (SDLCALL *init_track)(const void *data, size_t datalen, const SDL_AudioSpec *spec, SDL_PropertiesID metadata_props, void **userdata);
28+
bool (SDLCALL *init_audio)(const void *data, size_t datalen, SDL_AudioSpec *spec, SDL_PropertiesID props, void **audio_userdata); // see if it's a supported format, init spec, set metadata in props, allocate static userdata.
29+
bool (SDLCALL *init_track)(void *audio_userdata, const void *data, size_t datalen, const SDL_AudioSpec *spec, SDL_PropertiesID metadata_props, void **userdata);
3030
int (SDLCALL *decode)(void *userdata, void *buffer, size_t buflen);
3131
bool (SDLCALL *seek)(void *userdata, Uint64 frame);
3232
void (SDLCALL *quit_track)(void *userdata);
33+
void (SDLCALL *quit_audio)(void *audio_userdata);
3334
void (SDLCALL *quit)(void); // deinitialize the decoder (unload external libraries, etc).
3435
} Mix_Decoder;
3536

@@ -48,6 +49,7 @@ struct Mix_Audio
4849
size_t buflen;
4950
SDL_AudioSpec spec;
5051
const Mix_Decoder *decoder;
52+
void *decoder_userdata;
5153
Mix_Audio *prev; // double-linked list for all_audios.
5254
Mix_Audio *next;
5355
};
@@ -58,17 +60,17 @@ struct Mix_Track
5860
size_t input_buffer_len; // number of bytes allocated to input_buffer.
5961
Mix_Audio *input_audio; // non-NULL if used with Mix_SetTrackAudioStream. Holds a reference.
6062
SDL_AudioStream *input_stream; // non-NULL if used with Mix_SetTrackAudioStream. Not owned by SDL_mixer!
61-
void *decoder_state; // Mix_Decoder-specific data for this run, if any.
63+
void *decoder_userdata; // Mix_Decoder-specific data for this run, if any.
6264
SDL_AudioSpec input_spec; // data from input_stream or input_audio is in this format.
6365
SDL_AudioSpec output_spec; // processed data we send to SDL is in this format.
6466
SDL_AudioStream *output_stream; // the stream that is bound to the audio device.
6567
Mix_TrackState state; // playing, paused, stopped.
6668
Uint64 position; // sample frames played from start of file.
67-
Uint64 silence_frames; // number of frames of silence to mix at the end of the track.
69+
Sint64 silence_frames; // number of frames of silence to mix at the end of the track.
6870
Sint64 max_frames; // consider audio at EOF after this many sample frames.
6971
bool fire_and_forget; // true if this is a Mix_Track managed internally for fire-and-forget playback.
70-
int fade_frames; // fade in or out for this many sample frames.
71-
Uint64 fade_start_frame; // fade in or out starting on this sample frame.
72+
Sint64 total_fade_frames; // fade in or out for this many sample frames.
73+
Sint64 fade_frames; // remaining frames to fade.
7274
int fade_direction; // -1: fade out 0: don't fade 1: fade in
7375
int loops_remaining; // seek to loop_start and continue this many more times at end of input. Negative to loop forever.
7476
int loop_start; // sample frame position for loops to begin, so you can play an intro once and then loop from an internal point thereafter.
@@ -82,6 +84,13 @@ struct Mix_Track
8284
Mix_Track *fire_and_forget_next; // linked list for the fire-and-forget pool.
8385
};
8486

87+
// these are not (currently) available in the public API, and may change names or functionality, or be removed.
88+
#define MIX_PROP_DECODER_NAME_STRING "SDL_mixer.decoder.name"
89+
#define MIX_PROP_DECODER_FORMAT_NUMBER "SDL_mixer.decoder.format"
90+
#define MIX_PROP_DECODER_CHANNELS_NUMBER "SDL_mixer.decoder.channels"
91+
#define MIX_PROP_DECODER_FREQ_NUMBER "SDL_mixer.decoder.format"
92+
93+
8594

8695
// these might not all be available, but they are all declared here as if they are.
8796
extern Mix_Decoder Mix_Decoder_WAV;

src/decoder_raw.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
SDL_mixer: An audio mixer library based on the SDL library
3+
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4+
5+
This software is provided 'as-is', without any express or implied
6+
warranty. In no event will the authors be held liable for any damages
7+
arising from the use of this software.
8+
9+
Permission is granted to anyone to use this software for any purpose,
10+
including commercial applications, and to alter it and redistribute it
11+
freely, subject to the following restrictions:
12+
13+
1. The origin of this software must not be misrepresented; you must not
14+
claim that you wrote the original software. If you use this software
15+
in a product, an acknowledgment in the product documentation would be
16+
appreciated but is not required.
17+
2. Altered source versions must be plainly marked as such, and must not be
18+
misrepresented as being the original software.
19+
3. This notice may not be removed or altered from any source distribution.
20+
*/
21+
22+
#include "SDL_mixer_internal.h"
23+
24+
typedef struct RAW_UserData
25+
{
26+
const Uint8 *data;
27+
size_t datalen;
28+
size_t position;
29+
size_t framesize;
30+
size_t total_frames;
31+
} RAW_UserData;
32+
33+
static bool RAW_init(void)
34+
{
35+
return true; // always succeeds.
36+
}
37+
38+
static bool RAW_init_audio(const void *data, size_t datalen, SDL_AudioSpec *spec, SDL_PropertiesID props, void **audio_userdata)
39+
{
40+
const char *decoder_name = SDL_GetStringProperty(props, MIX_PROP_AUDIO_DECODER_STRING, NULL);
41+
if (!decoder_name || (SDL_strcasecmp(decoder_name, "raw") != 0)) {
42+
return false;
43+
}
44+
45+
const Sint64 si64fmt = SDL_GetNumberProperty(props, MIX_PROP_DECODER_FORMAT_NUMBER, -1);
46+
const Sint64 si64channels = SDL_GetNumberProperty(props, MIX_PROP_DECODER_CHANNELS_NUMBER, -1);
47+
const Sint64 si64freq = SDL_GetNumberProperty(props, MIX_PROP_DECODER_FREQ_NUMBER, -1);
48+
49+
if ((si64fmt <= 0) || (si64channels <= 0) || (si64freq <= 0)) {
50+
return false;
51+
}
52+
53+
spec->format = (SDL_AudioFormat) si64fmt;
54+
spec->channels = (int) si64channels;
55+
spec->freq = (int) si64freq;
56+
57+
// we don't have to inspect the data, we treat anything as valid.
58+
*audio_userdata = NULL;
59+
60+
return true;
61+
}
62+
63+
static bool RAW_init_track(void *audio_userdata, const void *data, size_t datalen, const SDL_AudioSpec *spec, SDL_PropertiesID props, void **userdata)
64+
{
65+
RAW_UserData *d = (RAW_UserData *) SDL_calloc(1, sizeof (*d));
66+
if (!d) {
67+
return false;
68+
}
69+
70+
// Clamp data to complete sample frames, just in case.
71+
d->data = data;
72+
d->framesize = SDL_AUDIO_FRAMESIZE(*spec);
73+
d->total_frames = datalen / d->framesize;
74+
d->datalen = d->total_frames * d->framesize;
75+
*userdata = d;
76+
77+
return true;
78+
}
79+
80+
static int RAW_decode(void *userdata, void *buffer, size_t buflen)
81+
{
82+
RAW_UserData *d = (RAW_UserData *) userdata;
83+
const size_t remaining = d->datalen - d->position;
84+
const size_t cpy = SDL_min(buflen, remaining);
85+
if (cpy) {
86+
SDL_memcpy(buffer, d->data + d->position, cpy);
87+
d->position += cpy;
88+
}
89+
return cpy;
90+
}
91+
92+
static bool RAW_seek(void *userdata, Uint64 frame)
93+
{
94+
RAW_UserData *d = (RAW_UserData *) userdata;
95+
if (frame > d->total_frames) {
96+
return SDL_SetError("Seek past end of data");
97+
}
98+
d->position = (size_t) (frame * d->framesize);
99+
return true;
100+
}
101+
102+
static void RAW_quit_track(void *userdata)
103+
{
104+
SDL_free(userdata);
105+
}
106+
107+
static void RAW_quit_audio(void *audio_userdata)
108+
{
109+
SDL_assert(!audio_userdata);
110+
}
111+
112+
static void RAW_quit(void)
113+
{
114+
// no-op.
115+
}
116+
117+
Mix_Decoder Mix_Decoder_RAW = {
118+
"RAW",
119+
RAW_init,
120+
RAW_init_audio,
121+
RAW_init_track,
122+
RAW_decode,
123+
RAW_seek,
124+
RAW_quit_track,
125+
RAW_quit_audio,
126+
RAW_quit
127+
};
128+

src/decoder_wav.c

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,109 @@
2121

2222
#include "SDL_mixer_internal.h"
2323

24+
typedef struct WAV_AudioUserData
25+
{
26+
Uint8 *data;
27+
Uint32 datalen;
28+
} WAV_AudioUserData;
29+
30+
typedef struct WAV_UserData
31+
{
32+
const Uint8 *data;
33+
size_t datalen;
34+
size_t position;
35+
size_t framesize;
36+
size_t total_frames;
37+
} WAV_UserData;
38+
39+
2440
static bool WAV_init(void)
25-
static bool WAV_prepare_audio(const void *data, size_t datalen, SDL_AudioSpec *spec, SDL_PropertiesID props)
26-
static bool WAV_init_track(const void *data, size_t datalen, const SDL_AudioSpec *spec, SDL_PropertiesID metadata_props, void **userdata)
27-
static int WAV_decode(void *userdata, void *buffer, size_t buflen)
41+
{
42+
return true; // already available without external dependencies.
43+
}
44+
45+
static bool WAV_init_audio(const void *data, size_t datalen, SDL_AudioSpec *spec, SDL_PropertiesID props, void **audio_userdata)
46+
{
47+
WAV_AudioUserData *d = (WAV_AudioUserData *) SDL_calloc(1, sizeof (*d));
48+
if (!d) {
49+
return false;
50+
}
51+
52+
// this is obviously wrong.
53+
if (!SDL_LoadWAV_IO(SDL_IOFromConstMem(data, datalen), true, spec, &d->data, &d->datalen)) {
54+
SDL_free(d);
55+
return false;
56+
}
57+
58+
*audio_userdata = d;
59+
60+
return true;
61+
}
62+
63+
static bool WAV_init_track(void *audio_userdata, const void *data, size_t datalen, const SDL_AudioSpec *spec, SDL_PropertiesID props, void **userdata)
64+
{
65+
WAV_AudioUserData *ad = (WAV_AudioUserData *) audio_userdata;
66+
WAV_UserData *d = (WAV_UserData *) SDL_calloc(1, sizeof (*d));
67+
if (!d) {
68+
return false;
69+
}
70+
71+
// Clamp data to complete sample frames, just in case.
72+
d->data = ad->data;
73+
d->framesize = SDL_AUDIO_FRAMESIZE(*spec);
74+
d->total_frames = ad->datalen / d->framesize;
75+
d->datalen = (size_t) (d->total_frames * d->framesize);
76+
*userdata = d;
77+
78+
return true;
79+
}
80+
81+
static int WAV_decode(void *userdata, void *buffer, size_t buflen)
82+
{
83+
WAV_UserData *d = (WAV_UserData *) userdata;
84+
const size_t remaining = d->datalen - d->position;
85+
const size_t cpy = SDL_min(buflen, remaining);
86+
if (cpy) {
87+
SDL_memcpy(buffer, d->data + d->position, cpy);
88+
d->position += cpy;
89+
}
90+
return cpy;
91+
}
92+
2893
static bool WAV_seek(void *userdata, Uint64 frame)
94+
{
95+
WAV_UserData *d = (WAV_UserData *) userdata;
96+
if (frame > d->total_frames) {
97+
return SDL_SetError("Seek past end of data");
98+
}
99+
d->position = (size_t) (frame * d->framesize);
100+
return true;
101+
}
102+
29103
static void WAV_quit_track(void *userdata)
104+
{
105+
SDL_free(userdata);
106+
}
107+
108+
static void WAV_quit_audio(void *audio_userdata)
109+
{
110+
SDL_free(audio_userdata);
111+
}
112+
30113
static void WAV_quit(void)
114+
{
115+
// no-op.
116+
}
31117

32118
Mix_Decoder Mix_Decoder_WAV = {
33119
"WAV",
34120
WAV_init,
121+
WAV_init_audio,
35122
WAV_init_track,
36123
WAV_decode,
37124
WAV_seek,
38125
WAV_quit_track,
126+
WAV_quit_audio,
39127
WAV_quit
40128
};
41129

src/make.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/sh
2+
3+
gcc -O0 -Wall -ggdb3 -I../include -o testmixer SDL_mixer.c decoder_wav.c decoder_raw.c testmixer.c `pkg-config sdl3 --cflags --libs`
4+

src/sample.wav

119 KB
Binary file not shown.

src/testmixer.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
2+
#include <SDL3/SDL.h>
3+
#include <SDL3/SDL_main.h>
4+
#include "SDL3_mixer/SDL_mixer.h"
5+
6+
//static SDL_Window *window = NULL;
7+
//static SDL_Renderer *renderer = NULL;
8+
static Mix_Track *track = NULL;
9+
10+
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
11+
{
12+
SDL_SetAppMetadata("Test SDL_mixer", "1.0", "org.libsdl.testmixer");
13+
14+
if (!SDL_Init(SDL_INIT_VIDEO)) { // it's safe to SDL_INIT_AUDIO, but SDL_mixer will do it for us.
15+
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
16+
return SDL_APP_FAILURE;
17+
// } else if (!SDL_CreateWindowAndRenderer("testmixer", 640, 480, 0, &window, &renderer)) {
18+
// SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
19+
// return SDL_APP_FAILURE;
20+
} else if (!Mix_OpenAudio(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL)) {
21+
SDL_Log("Couldn't create mixer: %s", SDL_GetError());
22+
return SDL_APP_FAILURE;
23+
}
24+
25+
Mix_Audio *audio = Mix_LoadAudio("sample.wav", false);
26+
track = Mix_CreateTrack();
27+
28+
Mix_SetTrackAudio(track, audio);
29+
Mix_PlayTrack(track, Mix_TrackMSToFrames(track, 9440), 3, 0, Mix_TrackMSToFrames(track, 6097), Mix_TrackMSToFrames(track, 30000), Mix_TrackMSToFrames(track, 3000));
30+
//Sint64 maxFrames, int loops, Sint64 startpos, Sint64 loop_start, Sint64 fadeIn, Sint64 append_silence_frames);
31+
32+
return SDL_APP_CONTINUE;
33+
}
34+
35+
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
36+
{
37+
if (event->type == SDL_EVENT_QUIT) {
38+
return SDL_APP_SUCCESS;
39+
}
40+
return SDL_APP_CONTINUE;
41+
}
42+
43+
SDL_AppResult SDL_AppIterate(void *appstate)
44+
{
45+
// SDL_RenderClear(renderer);
46+
// SDL_RenderPresent(renderer);
47+
return Mix_Playing(track) ? SDL_APP_CONTINUE : SDL_APP_SUCCESS;
48+
}
49+
50+
void SDL_AppQuit(void *appstate, SDL_AppResult result)
51+
{
52+
// SDL will clean up the window/renderer for us.
53+
// SDL_mixer will clean up the track and audio.
54+
Mix_CloseAudio();
55+
}
56+

0 commit comments

Comments
 (0)