102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
mss_stream.c
Go to the documentation of this file.
1#include "mss_private.h"
2#include "sidecar_private.h"
3
4#include <dttr_log.h>
5#include <dttr_path.h>
6
7#include <sds.h>
8
9#include <SDL3/SDL.h>
10#include <SDL3_mixer/SDL_mixer.h>
11
12#include <stdbool.h>
13#include <stdlib.h>
14#include <string.h>
15#include <windows.h>
16
17typedef struct mss_stream {
18 MIX_Audio *audio;
19 MIX_Track *track;
22 int volume;
23 int loops;
24 int status;
26
28
29static mss_stream *find_stream(const void *ptr) {
30 for (mss_stream *stream = streams; stream; stream = stream->next) {
31 if (stream == ptr) {
32 return stream;
33 }
34 }
35
36 return NULL;
37}
38
39// Returns the zero-based slot for an active SDL_mixer stream handle.
40static int stream_slot(const mss_stream *target_stream) {
41 int index = 0;
42
43 for (mss_stream *stream = streams; stream; stream = stream->next, index++) {
44 if (stream == target_stream) {
45 return index;
46 }
47 }
48
49 return -1;
50}
51
57
58// Combines per-stream volume with the master gain before updating the SDL_mixer track.
59static void apply_stream_gain(mss_stream *stream) {
60 if (!stream->track) {
61 return;
62 }
63
64 MIX_SetTrackGain(
65 stream->track,
67 stream->volume,
70 )
71 );
72}
73
74// Binds decoded audio to its track and reapplies gain after stream setup changes.
75static void apply_stream_track(mss_stream *stream) {
76 if (!stream->track || !stream->audio) {
77 return;
78 }
79
80 MIX_SetTrackAudio(stream->track, stream->audio);
81 apply_stream_gain(stream);
82}
83
84// Adds a newly opened stream to the intrusive list used for handle validation.
85static void link_stream(mss_stream *stream) {
86 stream->prev = NULL;
87 stream->next = streams;
88
89 if (streams) {
90 streams->prev = stream;
91 }
92
93 streams = stream;
94}
95
96// Removes a stream from the active list before its handle memory is cleared.
97static void unlink_stream(mss_stream *stream) {
98 if (stream->prev) {
99 stream->prev->next = stream->next;
100 } else if (streams == stream) {
101 streams = stream->next;
102 }
103
104 if (stream->next) {
105 stream->next->prev = stream->prev;
106 }
107
108 stream->prev = NULL;
109 stream->next = NULL;
110}
111
112// Releases SDL_mixer track and audio objects owned by one MSS stream handle.
113static void destroy_stream_objects(mss_stream *stream) {
114 if (stream->track) {
115 MIX_SetTrackAudio(stream->track, NULL);
116 MIX_DestroyTrack(stream->track);
117 stream->track = NULL;
118 }
119
120 if (stream->audio) {
121 MIX_DestroyAudio(stream->audio);
122 stream->audio = NULL;
123 }
124}
125
126// Unlinks, releases, clears, and frees one stream handle returned to the game.
127static void destroy_stream(mss_stream *stream) {
128 unlink_stream(stream);
130 memset(stream, 0, sizeof(*stream));
131 free(stream);
132}
133
134// Releases all active SDL_mixer stream handles.
136 while (streams) {
138 }
139}
140
141// Reapplies master gain to every active SDL_mixer stream.
143 for (mss_stream *stream = streams; stream; stream = stream->next) {
144 apply_stream_gain(stream);
145 }
146}
147
148// Normalizes absolute and relative stream paths into the file path SDL_mixer should load.
149static sds resolve_stream_path(const char *path) {
150 if (!path) {
151 return sdsempty();
152 }
153
154 const bool absolute = DTTR_Path_IsWindowsAbsolute(path);
155 const char *relative = absolute ? dttr_game_data_find_data_segment(path + 3) : path;
156 sds resolved = relative ? dttr_game_data_resolve_media_path(relative) : sdsnew(path);
157
158 if (!resolved) {
159 return sdsempty();
160 }
161
162 sdsmapchars(resolved, "/", "\\", 1);
163 return resolved;
164}
165
166// Opens an SDL_mixer stream for the Miles AIL stream API.
167void *__stdcall dttr_mss_ail_open_stream(void *driver, const char *path, int stream_mem) {
169 "MSS AIL_open_stream(driver=%p, path=\"%s\", stream_mem=%d)",
170 driver,
171 path ? path : "(null)",
172 stream_mem
173 );
174
175 if (!path || !dttr_mss_core_ensure_mixer()) {
176 DTTR_LOG_TRACE("MSS AIL_open_stream -> NULL (invalid path or mixer unavailable)");
177 return NULL;
178 }
179
180 mss_stream *stream = calloc(1, sizeof(mss_stream));
181
182 if (!stream) {
183 return NULL;
184 }
185
186 reset_stream_defaults(stream);
187
188 sds open_path = resolve_stream_path(path);
189 DTTR_LOG_TRACE("MSS AIL_open_stream resolved path=\"%s\"", open_path);
190
191 stream->audio = MIX_LoadAudio(dttr_mss_core_mixer(), open_path, false);
192
193 if (!stream->audio) {
195 "MIX_LoadAudio stream failed for %s resolved as %s: %s",
196 path,
197 open_path,
198 SDL_GetError()
199 );
200
201 sdsfree(open_path);
202 free(stream);
203 return NULL;
204 }
205
206 sdsfree(open_path);
207
208 stream->track = MIX_CreateTrack(dttr_mss_core_mixer());
209
210 if (!stream->track) {
211 DTTR_LOG_ERROR("MIX_CreateTrack stream failed: %s", SDL_GetError());
213 free(stream);
214 return NULL;
215 }
216
217 apply_stream_track(stream);
218 link_stream(stream);
220 "MSS AIL_open_stream -> stream[%d]=%p track=%p audio=%p",
221 stream_slot(stream),
222 stream,
223 stream->track,
224 stream->audio
225 );
226 return stream;
227}
228
229// Closes an SDL_mixer stream returned to the Miles AIL stream API.
230void __stdcall dttr_mss_ail_close_stream(void *stream_ptr) {
231 mss_stream *stream = find_stream(stream_ptr);
232
233 if (!stream) {
234 return;
235 }
236
237 DTTR_LOG_TRACE("MSS AIL_close_stream(stream[%d]=%p)", stream_slot(stream), stream);
238 destroy_stream(stream);
239}
240
241// Starts playback for an SDL_mixer stream handle.
242void __stdcall dttr_mss_ail_start_stream(void *stream_ptr) {
243 mss_stream *stream = find_stream(stream_ptr);
244
245 if (!stream) {
246 return;
247 }
248
250 "MSS AIL_start_stream(stream[%d]=%p status=%d loops=%d volume=%d)",
251 stream_slot(stream),
252 stream,
253 stream->status,
254 stream->loops,
255 stream->volume
256 );
257 apply_stream_track(stream);
259 const int sdl_loops = dttr_mss_loops_to_sdl(stream->loops);
260 dttr_mss_track_play(stream->track, sdl_loops);
262 "MSS AIL_start_stream stream[%d] played sdl_loops=%d",
263 stream_slot(stream),
264 sdl_loops
265 );
266}
267
268// Reports the Miles-compatible status for an SDL_mixer stream handle.
269int __stdcall dttr_mss_ail_stream_status(void *stream_ptr) {
270 mss_stream *stream = find_stream(stream_ptr);
271
272 if (!stream) {
274 }
275
276 stream->status = dttr_mss_track_status(stream->track, stream->status);
277 return stream->status;
278}
279
280// Pauses or resumes an SDL_mixer stream handle.
281void __stdcall dttr_mss_ail_pause_stream(void *stream_ptr, int pause) {
282 mss_stream *stream = find_stream(stream_ptr);
283
284 if (!stream) {
285 return;
286 }
287
288 if (pause) {
289 MIX_PauseTrack(stream->track);
290 } else {
291 MIX_ResumeTrack(stream->track);
292 }
293
295}
296
297// Applies Miles stream volume to an SDL_mixer stream handle.
298void __stdcall dttr_mss_ail_set_stream_volume(void *stream_ptr, int volume) {
299 mss_stream *stream = find_stream(stream_ptr);
300
301 if (!stream) {
302 return;
303 }
304
305 stream->volume = volume;
306 apply_stream_gain(stream);
307}
308
309// Applies Miles loop count to an SDL_mixer stream handle.
310void __stdcall dttr_mss_ail_set_stream_loop_count(void *stream_ptr, int loops) {
311 mss_stream *stream = find_stream(stream_ptr);
312
313 if (!stream) {
314 return;
315 }
316
317 stream->loops = loops;
318 MIX_SetTrackLoops(stream->track, dttr_mss_loops_to_sdl(loops));
319}
void DWORD DWORD * free
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
#define DTTR_LOG_TRACE(...)
Definition dttr_log.h:27
#define DTTR_LOG_ERROR(...)
Definition dttr_log.h:31
bool DTTR_Path_IsWindowsAbsolute(const char *path)
Definition path.c:175
sds dttr_game_data_resolve_media_path(const char *relative)
Definition game_data.c:210
const char * dttr_game_data_find_data_segment(const char *path)
Definition game_data.c:144
float dttr_mss_core_master_gain()
Definition mss_core.c:184
bool dttr_mss_core_ensure_mixer()
Definition mss_core.c:93
MIX_Mixer * dttr_mss_core_mixer()
Definition mss_core.c:146
static int dttr_mss_loops_to_sdl(int mss_loop_count)
Definition mss_private.h:88
#define DTTR_MSS_STATUS_PLAYING
Definition mss_private.h:16
#define DTTR_MSS_DEFAULT_VOLUME
Definition mss_private.h:25
void dttr_mss_track_play(MIX_Track *track, int sdl_loops)
Definition mss_track.c:38
#define DTTR_MSS_STREAM_HEADROOM_GAIN
Definition mss_private.h:35
#define DTTR_MSS_STATUS_DONE
Definition mss_private.h:15
float dttr_mss_track_gain(int volume, float master_gain, float headroom)
Definition mss_track.c:9
int dttr_mss_track_status(MIX_Track *track, int previous_status)
Definition mss_track.c:71
#define DTTR_MSS_DEFAULT_LOOP_COUNT
Definition mss_private.h:22
static mss_stream * streams
Definition mss_stream.c:27
static void unlink_stream(mss_stream *stream)
Definition mss_stream.c:97
void dttr_mss_ail_set_stream_loop_count(void *stream_ptr, int loops)
Definition mss_stream.c:310
static sds resolve_stream_path(const char *path)
Definition mss_stream.c:149
static mss_stream * find_stream(const void *ptr)
Definition mss_stream.c:29
static void apply_stream_track(mss_stream *stream)
Definition mss_stream.c:75
static void apply_stream_gain(mss_stream *stream)
Definition mss_stream.c:59
static void destroy_stream(mss_stream *stream)
Definition mss_stream.c:127
void dttr_mss_ail_set_stream_volume(void *stream_ptr, int volume)
Definition mss_stream.c:298
void * dttr_mss_ail_open_stream(void *driver, const char *path, int stream_mem)
Definition mss_stream.c:167
void dttr_mss_ail_start_stream(void *stream_ptr)
Definition mss_stream.c:242
void dttr_mss_ail_pause_stream(void *stream_ptr, int pause)
Definition mss_stream.c:281
void dttr_mss_stream_shutdown_all()
Definition mss_stream.c:135
void dttr_mss_ail_close_stream(void *stream_ptr)
Definition mss_stream.c:230
void dttr_mss_stream_apply_master_gain()
Definition mss_stream.c:142
static void reset_stream_defaults(mss_stream *stream)
Definition mss_stream.c:52
static void link_stream(mss_stream *stream)
Definition mss_stream.c:85
static int stream_slot(const mss_stream *target_stream)
Definition mss_stream.c:40
int dttr_mss_ail_stream_status(void *stream_ptr)
Definition mss_stream.c:269
static void destroy_stream_objects(mss_stream *stream)
Definition mss_stream.c:113
MIX_Track * track
Definition mss_stream.c:19
struct mss_stream * prev
Definition mss_stream.c:20
MIX_Audio * audio
Definition mss_stream.c:18
struct mss_stream * next
Definition mss_stream.c:21