Skip to content

Changed frames/ms types from Uint64 to Sint64 #718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 34 additions & 24 deletions include/SDL3_mixer/SDL_mixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ extern SDL_DECLSPEC void SDLCALL MIX_UntagTrack(MIX_Track *track, const char *ta
*
* \sa MIX_GetTrackPlaybackPosition
*/
extern SDL_DECLSPEC bool SDLCALL MIX_SetTrackPlaybackPosition(MIX_Track *track, Uint64 frames);
extern SDL_DECLSPEC bool SDLCALL MIX_SetTrackPlaybackPosition(MIX_Track *track, Sint64 frames);

/**
* Get the current input position of a playing track.
Expand Down Expand Up @@ -1359,23 +1359,23 @@ extern SDL_DECLSPEC Sint64 SDLCALL MIX_GetTrackRemaining(MIX_Track *track);
* mid-stream (for example, if decoding a file that is two MP3s concatenated
* together).
*
* If the track has no input, this returns 0.
*
* On various errors (MIX_Init() was not called, the track is NULL), this
* returns 0, but there is no mechanism to distinguish errors from tracks
* without a valid input.
* 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 zero for errors/no input.
* \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_TrackFramesToMS
*/
extern SDL_DECLSPEC Uint64 SDLCALL MIX_TrackMSToFrames(MIX_Track *track, Uint64 ms);
extern SDL_DECLSPEC Sint64 SDLCALL MIX_TrackMSToFrames(MIX_Track *track, Sint64 ms);

/**
* Convert sample frames for a track's current format to milliseconds.
Expand All @@ -1388,41 +1388,44 @@ extern SDL_DECLSPEC Uint64 SDLCALL MIX_TrackMSToFrames(MIX_Track *track, Uint64
* Sample frames are more precise than milliseconds, so out of necessity, this
* function will approximate by rounding down to the closest full millisecond.
*
* If the track has no input, this returns 0.
*
* On various errors (MIX_Init() was not called, the track is NULL), this
* returns 0, but there is no mechanism to distinguish errors from tracks
* without a valid input.
* 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 zero for errors/no input.
* \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.
*
* \sa MIX_TrackMSToFrames
*/
extern SDL_DECLSPEC Uint64 SDLCALL MIX_TrackFramesToMS(MIX_Track *track, Uint64 frames);
extern SDL_DECLSPEC Sint64 SDLCALL MIX_TrackFramesToMS(MIX_Track *track, Sint64 frames);

/**
* 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 zero for errors/no input.
* \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_AudioFramesToMS
*/
extern SDL_DECLSPEC Uint64 SDLCALL MIX_AudioMSToFrames(MIX_Audio *audio, Uint64 ms);
extern SDL_DECLSPEC Sint64 SDLCALL MIX_AudioMSToFrames(MIX_Audio *audio, Sint64 ms);

/**
* Convert sample frames for a MIX_Audio's format to milliseconds.
Expand All @@ -1433,54 +1436,61 @@ extern SDL_DECLSPEC Uint64 SDLCALL MIX_AudioMSToFrames(MIX_Audio *audio, Uint64
* Sample frames are more precise than milliseconds, so out of necessity, this
* function will approximate by rounding down to the closest full millisecond.
*
* If `frames` is < 0, this returns -1.
*
* \param audio the audio to query.
* \param frames the audio-specific sample frames to convert to milliseconds.
* \returns Converted number of milliseconds, or zero for errors/no input.
* \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.
*
* \sa MIX_AudioMSToFrames
*/
extern SDL_DECLSPEC Uint64 SDLCALL MIX_AudioFramesToMS(MIX_Audio *audio, Uint64 frames);
extern SDL_DECLSPEC Sint64 SDLCALL MIX_AudioFramesToMS(MIX_Audio *audio, Sint64 frames);

/**
* Convert milliseconds to sample frames at a specific sample rate.
*
* If `sample_rate` is <= 0, this returns 0. No error is set.
* If `sample_rate` is <= 0, this returns -1.
* If `ms` is < 0, this returns -1.
*
* \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 zero for errors.
* \returns Converted number of sample frames, 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_FramesToMS
*/
extern SDL_DECLSPEC Uint64 SDLCALL MIX_MSToFrames(int sample_rate, Uint64 ms);
extern SDL_DECLSPEC Sint64 SDLCALL MIX_MSToFrames(int sample_rate, Sint64 ms);

/**
* Convert sample frames, at a specific sample rate, to milliseconds.
*
* If `sample_rate` is <= 0, this returns 0. No error is set.
*
* 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 zero for errors.
* \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
*/
extern SDL_DECLSPEC Uint64 SDLCALL MIX_FramesToMS(int sample_rate, Uint64 frames);
extern SDL_DECLSPEC Sint64 SDLCALL MIX_FramesToMS(int sample_rate, Sint64 frames);


/* operations that deal with actual mixing/playback... */
Expand Down
62 changes: 42 additions & 20 deletions src/SDL_mixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1770,10 +1770,12 @@ void MIX_UntagTrack(MIX_Track *track, const char *tag)
SDL_UnlockProperties(tags);
}

bool MIX_SetTrackPlaybackPosition(MIX_Track *track, Uint64 frames)
bool MIX_SetTrackPlaybackPosition(MIX_Track *track, Sint64 frames)
{
if (!CheckTrackParam(track)) {
return false;
} else if (frames < 0) {
return SDL_InvalidParamError("frames");
}

bool retval = true;
Expand All @@ -1787,10 +1789,10 @@ bool MIX_SetTrackPlaybackPosition(MIX_Track *track, Uint64 frames)
retval = SDL_SetError("No audio currently assigned to this track");
}
} else {
retval = track->input_audio->decoder->seek(track->decoder_userdata, frames);
retval = track->input_audio->decoder->seek(track->decoder_userdata, (Uint64) frames);
if (retval) {
SDL_ClearAudioStream(track->input_stream); // make sure that any extra buffered input from before the seek is removed.
track->position = frames;
track->position = (Uint64) frames;
}
}
UnlockTrack(track);
Expand Down Expand Up @@ -1860,25 +1862,33 @@ SDL_AudioStream *MIX_GetTrackAudioStream(MIX_Track *track)
return retval;
}

Uint64 MIX_MSToFrames(int sample_rate, Uint64 ms)
Sint64 MIX_MSToFrames(int sample_rate, Sint64 ms)
{
if (sample_rate <= 0) {
return 0;
SDL_InvalidParamError("sample_rate");
return -1;
} else if (ms < 0) {
SDL_InvalidParamError("ms");
return -1;
}
return (Uint64) ((((double) ms) / 1000.0) * ((double) sample_rate));
return (Sint64) ((((double) ms) / 1000.0) * ((double) sample_rate));
}

Uint64 MIX_FramesToMS(int sample_rate, Uint64 frames)
Sint64 MIX_FramesToMS(int sample_rate, Sint64 frames)
{
if (sample_rate <= 0) {
return 0;
SDL_InvalidParamError("sample_rate");
return -1;
} else if (frames < 0) {
SDL_InvalidParamError("frames");
return -1;
}
return (Uint64) ((((double) frames) / ((double) sample_rate)) * 1000.0);
return (Sint64) ((((double) frames) / ((double) sample_rate)) * 1000.0);
}

Uint64 MIX_TrackMSToFrames(MIX_Track *track, Uint64 ms)
Sint64 MIX_TrackMSToFrames(MIX_Track *track, Sint64 ms)
{
Uint64 retval = 0;
Sint64 retval = -1;
if (CheckTrackParam(track)) {
LockTrack(track);
SDL_AudioSpec spec;
Expand All @@ -1895,9 +1905,9 @@ Uint64 MIX_TrackMSToFrames(MIX_Track *track, Uint64 ms)
return retval;
}

Uint64 MIX_TrackFramesToMS(MIX_Track *track, Uint64 frames)
Sint64 MIX_TrackFramesToMS(MIX_Track *track, Sint64 frames)
{
Uint64 retval = 0;
Sint64 retval = -1;
if (CheckTrackParam(track)) {
LockTrack(track);
SDL_AudioSpec spec;
Expand All @@ -1914,18 +1924,18 @@ Uint64 MIX_TrackFramesToMS(MIX_Track *track, Uint64 frames)
return retval;
}

Uint64 MIX_AudioMSToFrames(MIX_Audio *audio, Uint64 ms)
Sint64 MIX_AudioMSToFrames(MIX_Audio *audio, Sint64 ms)
{
if (!CheckAudioParam(audio)) {
return 0;
return -1;
}
return MIX_MSToFrames(audio->spec.freq, ms);
}

Uint64 MIX_AudioFramesToMS(MIX_Audio *audio, Uint64 frames)
Sint64 MIX_AudioFramesToMS(MIX_Audio *audio, Sint64 frames)
{
if (!CheckAudioParam(audio)) {
return 0;
return -1;
}
return MIX_FramesToMS(audio->spec.freq, frames);
}
Expand All @@ -1936,7 +1946,11 @@ static Sint64 GetTrackOptionFramesOrTicks(MIX_Track *track, SDL_PropertiesID opt
return SDL_GetNumberProperty(options, framesprop, defval);
} else if (SDL_HasProperty(options, msprop)) {
const Sint64 val = SDL_GetNumberProperty(options, msprop, defval);
return (val < 0) ? val : MIX_TrackMSToFrames(track, val);
Sint64 val_frames = MIX_TrackMSToFrames(track, val);
if (val_frames == -1) {
val_frames = 0;
}
return (val < 0) ? val : val_frames;
}
return defval;
}
Expand Down Expand Up @@ -2104,7 +2118,11 @@ bool MIX_StopAllTracks(MIX_Mixer *mixer, Sint64 fade_out_ms)
LockMixer(mixer); // lock the mixer so all tracks stop at the same time.

for (MIX_Track *track = mixer->all_tracks; track != NULL; track = track->next) {
StopTrack(track, (fade_out_ms > 0) ? MIX_TrackMSToFrames(track, fade_out_ms) : -1);
Sint64 fade_out_frames = MIX_TrackMSToFrames(track, fade_out_ms);
if (fade_out_frames == -1) {
fade_out_frames = 0;
}
StopTrack(track, (fade_out_ms > 0) ? fade_out_frames : -1);
}

UnlockMixer(mixer);
Expand All @@ -2127,7 +2145,11 @@ bool MIX_StopTag(MIX_Mixer *mixer, const char *tag, Sint64 fade_out_ms)

const size_t total = list->num_tracks;
for (size_t i = 0; i < total; i++) {
StopTrack(list->tracks[i], (fade_out_ms > 0) ? MIX_TrackMSToFrames(list->tracks[i], fade_out_ms) : -1);
Sint64 fade_out_frames = MIX_TrackMSToFrames(list->tracks[i], fade_out_ms);
if (fade_out_frames == -1) {
fade_out_frames = 0;
}
StopTrack(list->tracks[i], (fade_out_ms > 0) ? fade_out_frames : -1);
}

SDL_UnlockRWLock(list->rwlock);
Expand Down
7 changes: 5 additions & 2 deletions src/decoder_fluidsynth.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,10 +338,13 @@ static bool SDLCALL FLUIDSYNTH_seek(void *track_userdata, Uint64 frame)
return SDL_Unsupported();
#else
FLUIDSYNTH_TrackData *tdata = (FLUIDSYNTH_TrackData *) track_userdata;
const int ticks = (int) MIX_FramesToMS(tdata->freq, frame);
Sint64 ticks = MIX_FramesToMS(tdata->freq, frame);
if (ticks == -1) {
ticks = 0;
}

// !!! FIXME: docs say this will fail if a seek was requested and then a second seek happens before we play more of the midi file, since the first seek will still be in progress.
return (fluidsynth.fluid_player_seek(tdata->player, ticks) == FLUID_OK);
return (fluidsynth.fluid_player_seek(tdata->player, (int)ticks) == FLUID_OK);
#endif
}

Expand Down
5 changes: 4 additions & 1 deletion src/decoder_gme.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ static bool SDLCALL GME_init_audio(SDL_IOStream *io, SDL_AudioSpec *spec, SDL_Pr
if ((info->intro_length >= 0) && (info->loop_length > 0)) {
*duration_frames = MIX_DURATION_INFINITE;
} else if (info->length >= 0) {
*duration_frames = (Sint64) MIX_MSToFrames(spec->freq, info->length);
*duration_frames = MIX_MSToFrames(spec->freq, info->length);
if (*duration_frames == -1) {
*duration_frames = 0;
}
}

gme.gme_free_info(info);
Expand Down
10 changes: 8 additions & 2 deletions src/decoder_timidity.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ static bool SDLCALL TIMIDITY_init_audio(SDL_IOStream *io, SDL_AudioSpec *spec, S
}

song_length_in_frames = MIX_MSToFrames(spec->freq, Timidity_GetSongLength(song));
if (song_length_in_frames == -1) {
song_length_in_frames = 0;
}
Timidity_FreeSong(song);

*duration_frames = song_length_in_frames;
Expand Down Expand Up @@ -138,8 +141,11 @@ static bool SDLCALL TIMIDITY_decode(void *track_userdata, SDL_AudioStream *strea
static bool SDLCALL TIMIDITY_seek(void *track_userdata, Uint64 frame)
{
TIMIDITY_TrackData *tdata = (TIMIDITY_TrackData *) track_userdata;
const Uint32 ticks = (Uint32) MIX_FramesToMS(tdata->freq, frame);
Timidity_Seek(tdata->song, ticks); // !!! FIXME: this returns void, what happens if we seek past EOF?
Sint64 ticks = MIX_FramesToMS(tdata->freq, frame);
if (ticks == -1) {
ticks = 0;
}
Timidity_Seek(tdata->song, (Uint32)ticks); // !!! FIXME: this returns void, what happens if we seek past EOF?
return true;
}

Expand Down
11 changes: 9 additions & 2 deletions src/decoder_xmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,10 @@ static bool SDLCALL XMP_init_audio(SDL_IOStream *io, SDL_AudioSpec *spec, SDL_Pr
struct xmp_frame_info frame_info;
libxmp.xmp_get_frame_info(ctx, &frame_info);

*duration_frames = MIX_MSToFrames(spec->freq, (Uint64) frame_info.total_time); // closest we can get.
*duration_frames = MIX_MSToFrames(spec->freq, (Sint64) frame_info.total_time); // closest we can get.
if (*duration_frames == -1) {
*duration_frames = 0;
}

libxmp.xmp_stop_module(ctx);
libxmp.xmp_end_player(ctx);
Expand Down Expand Up @@ -256,7 +259,11 @@ static bool SDLCALL XMP_decode(void *track_userdata, SDL_AudioStream *stream)
static bool SDLCALL XMP_seek(void *track_userdata, Uint64 frame)
{
XMP_TrackData *tdata = (XMP_TrackData *) track_userdata;
const int err = libxmp.xmp_seek_time(tdata->ctx, (int) MIX_FramesToMS(tdata->freq, frame));
Sint64 ms = MIX_FramesToMS(tdata->freq, (Sint64) frame);
if (ms == -1) {
ms = 0;
}
const int err = libxmp.xmp_seek_time(tdata->ctx, (int) ms);
return err ? SetLibXmpError("xmp_seek_time", err) : true;
}

Expand Down
Loading