-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathsnd_xmp.c
691 lines (608 loc) · 25 KB
/
snd_xmp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
/*
Copyright (C) 2014 nyov <nyov@nexnode.net>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* libxmp is licensed under the terms of the Lesser General Public License 2.1
*/
#include "darkplaces.h"
#include "snd_main.h"
#include "snd_xmp.h"
#include "sound.h"
#ifdef LINK_TO_LIBXMP
#include <xmp.h>
#if ((XMP_VERCODE+0) < 0x040200)
#error libxmp version 4.2 or newer is required when linking to libxmp
#endif
/* libxmp API */
// Version and player information
#define qxmp_version xmp_version // const char *xmp_version
#define qxmp_vercode xmp_vercode // const unsigned int xmp_vercode
//#define qxmp_get_format_list xmp_get_format_list // char **xmp_get_format_list()
// Context creation
#define qxmp_create_context xmp_create_context // xmp_context xmp_create_context()
#define qxmp_free_context xmp_free_context // void xmp_free_context(xmp_context c)
// Module loading
//#define qxmp_test_module xmp_test_module // int xmp_test_module(char *path, struct xmp_test_info *test_info)
//#define qxmp_load_module xmp_load_module // int xmp_load_module(xmp_context c, char *path)
#define qxmp_load_module_from_memory xmp_load_module_from_memory // int xmp_load_module_from_memory(xmp_context c, void *mem, long size)
//#define qxmp_load_module_from_file xmp_load_module_from_file // int xmp_load_module_from_file(xmp_context c, FILE *f, long size)
#define qxmp_release_module xmp_release_module // void xmp_release_module(xmp_context c)
//#define qxmp_scan_module xmp_scan_module // void xmp_scan_module(xmp_context c)
#define qxmp_get_module_info xmp_get_module_info // void xmp_get_module_info(xmp_context c, struct xmp_module_info *info)
// Module playing
#define qxmp_start_player xmp_start_player // int xmp_start_player(xmp_context c, int rate, int format)
#define qxmp_play_frame xmp_play_frame // int xmp_play_frame(xmp_context c)
#define qxmp_play_buffer xmp_play_buffer // int xmp_play_buffer(xmp_context c, void *buffer, int size, int loop)
#define qxmp_get_frame_info xmp_get_frame_info // void xmp_get_frame_info(xmp_context c, struct xmp_frame_info *info)
#define qxmp_end_player xmp_end_player // void xmp_end_player(xmp_context c)
// Player control
//#define qxmp_next_position xmp_next_position // int xmp_next_position(xmp_context c)
//#define qxmp_prev_position xmp_prev_position// int xmp_prev_position(xmp_context c)
//#define qxmp_set_position xmp_set_position // int xmp_set_position(xmp_context c, int pos)
//#define qxmp_stop_module xmp_stop_module // void xmp_stop_module(xmp_context c)
//#define qxmp_restart_module xmp_restart_module // void xmp_restart_module(xmp_context c)
//#define qxmp_seek_time xmp_seek_time // int xmp_seek_time(xmp_context c, int time)
//#define qxmp_channel_mute xmp_channel_mute // int xmp_channel_mute(xmp_context c, int channel, int status)
//#define qxmp_channel_vol xmp_channel_vol // int xmp_channel_vol(xmp_context c, int channel, int vol)
//#define qxmp_inject_event xmp_inject_event // void xmp_inject_event(xmp_context c, int channel, struct xmp_event *event)
// Player parameter setting
//#define qxmp_set_instrument_path xmp_set_instrument_path // int xmp_set_instrument_path(xmp_context c, char *path)
#define qxmp_get_player xmp_get_player // int xmp_get_player(xmp_context c, int param)
#define qxmp_set_player xmp_set_player // int xmp_set_player(xmp_context c, int param, int val)
#define xmp_dll 1
qbool XMP_OpenLibrary (void) {return true;}
void XMP_CloseLibrary (void) {}
#else
/* libxmp ABI */
/*
=================================================================
definitions from xmp.h
=================================================================
*/
// constants from libxmp
#define XMP_NAME_SIZE 64 /* Size of module name and type */
/* sample format flags */
#define XMP_FORMAT_8BIT (1 << 0) /* Mix to 8-bit instead of 16 */
#define XMP_FORMAT_UNSIGNED (1 << 1) /* Mix to unsigned samples */
#define XMP_FORMAT_MONO (1 << 2) /* Mix to mono instead of stereo */
/* player parameters */
#define XMP_PLAYER_AMP 0 /* Amplification factor */
#define XMP_PLAYER_MIX 1 /* Stereo mixing */
#define XMP_PLAYER_INTERP 2 /* Interpolation type */
#define XMP_PLAYER_DSP 3 /* DSP effect flags */
#define XMP_PLAYER_FLAGS 4 /* Player flags */
#define XMP_PLAYER_CFLAGS 5 /* Player flags for current module */
#define XMP_PLAYER_SMPCTL 6 /* Sample control flags */
#define XMP_PLAYER_VOLUME 7 /* Player module volume */
#define XMP_PLAYER_STATE 8 /* Internal player state */
#define XMP_PLAYER_SMIX_VOLUME 9 /* SMIX volume */
#define XMP_PLAYER_DEFPAN 10 /* Default pan setting */
/* interpolation types */
#define XMP_INTERP_NEAREST 0 /* Nearest neighbor */
#define XMP_INTERP_LINEAR 1 /* Linear (default) */
#define XMP_INTERP_SPLINE 2 /* Cubic spline */
/* player state */
#define XMP_STATE_UNLOADED 0 /* Context created */
#define XMP_STATE_LOADED 1 /* Module loaded */
#define XMP_STATE_PLAYING 2 /* Module playing */
/* sample flags */
#define XMP_SMPCTL_SKIP (1 << 0) /* Don't load samples */
/* limits */
//#define XMP_MAX_KEYS 121 /* Number of valid keys */
//#define XMP_MAX_ENV_POINTS 32 /* Max number of envelope points */
#define XMP_MAX_MOD_LENGTH 256 /* Max number of patterns in module */
//#define XMP_MAX_CHANNELS 64 /* Max number of channels in module */
#define XMP_MAX_SRATE 49170 /* max sampling rate (Hz) */
#define XMP_MIN_SRATE 4000 /* min sampling rate (Hz) */
//#define XMP_MIN_BPM 20 /* min BPM */
#define XMP_MAX_FRAMESIZE (5 * XMP_MAX_SRATE * 2 / XMP_MIN_BPM)
/* error codes */
#define XMP_END 1
#define XMP_ERROR_INTERNAL 2 /* Internal error */
#define XMP_ERROR_FORMAT 3 /* Unsupported module format */
#define XMP_ERROR_LOAD 4 /* Error loading file */
#define XMP_ERROR_DEPACK 5 /* Error depacking file */
#define XMP_ERROR_SYSTEM 6 /* System error */
#define XMP_ERROR_INVALID 7 /* Invalid parameter */
#define XMP_ERROR_STATE 8 /* Invalid player state */
// types from libxmp
typedef char *xmp_context;
static const char **qxmp_version;
static const unsigned int *qxmp_vercode;
struct xmp_channel {
int pan; /* Channel pan (0x80 is center) */
int vol; /* Channel volume */
#define XMP_CHANNEL_SYNTH (1 << 0) /* Channel is synthesized */
#define XMP_CHANNEL_MUTE (1 << 1) /* Channel is muted */
int flg; /* Channel flags */
};
//struct xmp_sequence {
// int entry_point;
// int duration;
//};
struct xmp_module {
char name[XMP_NAME_SIZE]; /* Module title */
char type[XMP_NAME_SIZE]; /* Module format */
int pat; /* Number of patterns */
int trk; /* Number of tracks */
int chn; /* Tracks per pattern */
int ins; /* Number of instruments */
int smp; /* Number of samples */
int spd; /* Initial speed */
int bpm; /* Initial BPM */
int len; /* Module length in patterns */
int rst; /* Restart position */
int gvl; /* Global volume */
struct xmp_pattern **xxp; /* Patterns */
struct xmp_track **xxt; /* Tracks */
struct xmp_instrument *xxi; /* Instruments */
struct xmp_sample *xxs; /* Samples */
struct xmp_channel xxc[64]; /* Channel info */
unsigned char xxo[XMP_MAX_MOD_LENGTH]; /* Orders */
};
//struct xmp_test_info {
// char name[XMP_NAME_SIZE]; /* Module title */
// char type[XMP_NAME_SIZE]; /* Module format */
//};
struct xmp_module_info {
unsigned char md5[16]; /* MD5 message digest */
int vol_base; /* Volume scale */
struct xmp_module *mod; /* Pointer to module data */
char *comment; /* Comment text, if any */
int num_sequences; /* Number of valid sequences */
struct xmp_sequence *seq_data; /* Pointer to sequence data */
};
struct xmp_frame_info
// { /* Current frame information */
// int pos; /* Current position */
// int pattern; /* Current pattern */
// int row; /* Current row in pattern */
// int num_rows; /* Number of rows in current pattern */
// int frame; /* Current frame */
// int speed; /* Current replay speed */
// int bpm; /* Current bpm */
// int time; /* Current module time in ms */
// int total_time; /* Estimated replay time in ms*/
// int frame_time; /* Frame replay time in us */
// void *buffer; /* Pointer to sound buffer */
// int buffer_size; /* Used buffer size */
// int total_size; /* Total buffer size */
// int volume; /* Current master volume */
// int loop_count; /* Loop counter */
// int virt_channels; /* Number of virtual channels */
// int virt_used; /* Used virtual channels */
// int sequence; /* Current sequence */
//
// struct xmp_channel_info { /* Current channel information */
// unsigned int period; /* Sample period */
// unsigned int position; /* Sample position */
// short pitchbend; /* Linear bend from base note*/
// unsigned char note; /* Current base note number */
// unsigned char instrument; /* Current instrument number */
// unsigned char sample; /* Current sample number */
// unsigned char volume; /* Current volume */
// unsigned char pan; /* Current stereo pan */
// unsigned char reserved; /* Reserved */
// struct xmp_event event; /* Current track event */
// } channel_info[XMP_MAX_CHANNELS];
//}
;
// Functions exported from libxmp
static xmp_context (*qxmp_create_context) (void);
static void (*qxmp_free_context) (xmp_context);
//static int (*qxmp_test_module) (char *, struct xmp_test_info *);
//static int (*qxmp_load_module) (xmp_context, char *);
//static void (*qxmp_scan_module) (xmp_context);
static void (*qxmp_release_module) (xmp_context);
static int (*qxmp_start_player) (xmp_context, int, int);
static int (*qxmp_play_frame) (xmp_context);
static int (*qxmp_play_buffer) (xmp_context, void *, int, int);
static void (*qxmp_get_frame_info) (xmp_context, struct xmp_frame_info *);
static void (*qxmp_end_player) (xmp_context);
//static void (*qxmp_inject_event) (xmp_context, int, struct xmp_event *);
static void (*qxmp_get_module_info) (xmp_context, struct xmp_module_info *);
//static char **(*qxmp_get_format_list) (void); // FIXME: did I do this right?
//static int (*qxmp_next_position) (xmp_context);
//static int (*qxmp_prev_position) (xmp_context);
//static int (*qxmp_set_position) (xmp_context, int);
//static void (*qxmp_stop_module) (xmp_context);
//static void (*qxmp_restart_module) (xmp_context);
//static int (*qxmp_seek_time) (xmp_context, int);
//static int (*qxmp_channel_mute) (xmp_context, int, int);
//static int (*qxmp_channel_vol) (xmp_context, int, int);
static int (*qxmp_set_player) (xmp_context, int, int);
static int (*qxmp_get_player) (xmp_context, int);
//static int (*qxmp_set_instrument_path) (xmp_context, char *);
static int (*qxmp_load_module_from_memory) (xmp_context, void *, long);
//static int (*qxmp_load_module_from_file) (xmp_context, void *, long);
//static int (XMP_EXPORT *qxmp_load_module_from_file) (xmp_context, void *, long);
/* External sample mixer API */
/*
static int (*qxmp_start_smix) (xmp_context, int, int);
static void (*qxmp_end_smix) (xmp_context);
static int (*qxmp_smix_play_instrument)(xmp_context, int, int, int, int);
static int (*qxmp_smix_play_sample) (xmp_context, int, int, int, int);
static int (*qxmp_smix_channel_pan) (xmp_context, int, int);
static int (*qxmp_smix_load_sample) (xmp_context, int, char *);
static int (*qxmp_smix_release_sample) (xmp_context, int);
// end Functions exported from libxmp
*/
/*
=================================================================
DarkPlaces definitions
=================================================================
*/
static dllfunction_t xmpfuncs[] =
{
/* libxmp ABI */
// Version and player information
{"xmp_version", (void **) &qxmp_version},
{"xmp_vercode", (void **) &qxmp_vercode},
// {"xmp_get_format_list", (void **) &qxmp_get_format_list},
// Context creation
{"xmp_create_context", (void **) &qxmp_create_context},
{"xmp_free_context", (void **) &qxmp_free_context},
// Module loading
// {"xmp_test_module", (void **) &qxmp_test_module},
// {"xmp_load_module", (void **) &qxmp_load_module},
{"xmp_load_module_from_memory", (void **) &qxmp_load_module_from_memory}, // since libxmp 4.2.0
// {"xmp_load_module_from_file", (void **) &qxmp_load_module_from_file}, // since libxmp 4.3.0
{"xmp_release_module", (void **) &qxmp_release_module},
// {"xmp_scan_module", (void **) &qxmp_scan_module},
{"xmp_get_module_info", (void **) &qxmp_get_module_info},
// Module playing
{"xmp_start_player", (void **) &qxmp_start_player},
{"xmp_play_frame", (void **) &qxmp_play_frame},
{"xmp_play_buffer", (void **) &qxmp_play_buffer},
{"xmp_get_frame_info", (void **) &qxmp_get_frame_info},
{"xmp_end_player", (void **) &qxmp_end_player},
// Player control
// {"xmp_next_position", (void **) &qxmp_next_position},
// {"xmp_prev_position", (void **) &qxmp_prev_position},
// {"xmp_set_position", (void **) &qxmp_set_position},
// {"xmp_stop_module", (void **) &qxmp_stop_module},
// {"xmp_restart_module", (void **) &qxmp_restart_module},
// {"xmp_seek_time", (void **) &qxmp_seek_time},
// {"xmp_channel_mute", (void **) &qxmp_channel_mute},
// {"xmp_channel_vol", (void **) &qxmp_channel_vol},
// {"xmp_inject_event", (void **) &qxmp_inject_event},
// Player parameter setting
// {"xmp_set_instrument_path", (void **) &qxmp_set_instrument_path},
{"xmp_get_player", (void **) &qxmp_get_player},
{"xmp_set_player", (void **) &qxmp_set_player},
/* smix */ // for completeness sake only, right now
// {"xmp_start_smix", (void **) &qxmp_start_smix},
// {"xmp_end_smix", (void **) &qxmp_end_smix},
// {"xmp_smix_play_instrument", (void **) &qxmp_smix_play_instrument},
// {"xmp_smix_play_sample", (void **) &qxmp_smix_play_sample},
// {"xmp_smix_channel_pan", (void **) &qxmp_smix_channel_pan},
// {"xmp_smix_load_sample", (void **) &qxmp_smix_load_sample},
// {"xmp_smix_release_sample", (void **) &qxmp_smix_release_sample},
{NULL, NULL}
};
// libxmp DLL handle
static dllhandle_t xmp_dll = NULL;
/*
=================================================================
DLL load & unload
=================================================================
*/
/*
====================
XMP_OpenLibrary
Try to load the libxmp DLL
====================
*/
qbool XMP_OpenLibrary (void)
{
const char* dllnames_xmp [] =
{
#if defined(WIN32)
"libxmp-4.dll",
"libxmp.dll",
#elif defined(MACOSX) // FIXME: untested, please test a mac os build
"libxmp.4.dylib",
"libxmp.dylib",
#else
"libxmp.so.4",
"libxmp.so",
#endif
NULL
};
if (xmp_dll) // Already loaded?
return true;
// COMMANDLINEOPTION: Sound: -noxmp disables xmp module sound support
if (Sys_CheckParm("-noxmp"))
return false;
// Load the DLL
if (Sys_LoadDependency (dllnames_xmp, &xmp_dll, xmpfuncs))
{
if (*qxmp_vercode < 0x040200)
{
Con_Printf("Found incompatible XMP library version %s, not loading. (4.2.0 or higher required)\n", *qxmp_version);
Sys_FreeLibrary (&xmp_dll);
return false;
}
if (developer_loading.integer >= 1)
Con_Printf("XMP library loaded, version %s (0x0%x)\n", *qxmp_version, *qxmp_vercode);
return true;
}
else
return false;
}
/*
====================
XMP_CloseLibrary
Unload the libxmp DLL
====================
*/
void XMP_CloseLibrary (void)
{
Sys_FreeLibrary (&xmp_dll);
}
#endif
/*
=================================================================
Module file decoding
=================================================================
*/
// Per-sfx data structure
typedef struct
{
unsigned char *file;
size_t filesize;
} xmp_stream_persfx_t;
// Per-channel data structure
typedef struct
{
xmp_context playercontext;
int bs;
int buffer_firstframe;
int buffer_numframes;
unsigned char buffer[STREAM_BUFFERSIZE*4];
} xmp_stream_perchannel_t;
/*
====================
XMP_GetSamplesFloat
====================
*/
static void XMP_GetSamplesFloat(channel_t *ch, sfx_t *sfx, int firstsampleframe, int numsampleframes, float *outsamplesfloat)
{
int i, len = numsampleframes * sfx->format.channels;
int f = sfx->format.width * sfx->format.channels; // bytes per frame in the buffer
xmp_stream_perchannel_t* per_ch = (xmp_stream_perchannel_t *)ch->fetcher_data;
xmp_stream_persfx_t* per_sfx = (xmp_stream_persfx_t *)sfx->fetcher_data;
const short *buf;
int newlength, done;
unsigned int format = 0;
// if this channel does not yet have a channel fetcher, make one
if (per_ch == NULL)
{
// allocate a struct to keep track of our file position and buffer
per_ch = (xmp_stream_perchannel_t *)Mem_Alloc(snd_mempool, sizeof(*per_ch));
// create an xmp file context
if ((per_ch->playercontext = qxmp_create_context()) == NULL)
{
//Con_Printf("error getting a libxmp file context; while trying to load file \"%s\"\n", filename);
Mem_Free(per_ch);
return;
}
// copy file to xmp
if (qxmp_load_module_from_memory(per_ch->playercontext, (void *)per_sfx->file, (long)per_sfx->filesize) < 0)
{
qxmp_free_context(per_ch->playercontext);
Mem_Free(per_ch);
return;
}
// start playing the loaded file
if (sfx->format.width == 1) { format |= XMP_FORMAT_8BIT | XMP_FORMAT_UNSIGNED; } // else 16bit
if (sfx->format.channels == 1) { format |= XMP_FORMAT_MONO; } // else stereo
if (qxmp_start_player(per_ch->playercontext, sfx->format.speed, format) < 0)
{
Mem_Free(per_ch);
return;
}
/* percentual left/right channel separation, default is 70. */
if (sfx->format.channels == 2 && (qxmp_set_player(per_ch->playercontext, XMP_PLAYER_MIX, 50) != 0))
{
Mem_Free(per_ch);
return;
}
/* interpolation type, default is XMP_INTERP_LINEAR */
if (qxmp_set_player(per_ch->playercontext, XMP_PLAYER_INTERP, XMP_INTERP_SPLINE) != 0)
{
Mem_Free(per_ch);
return;
}
per_ch->bs = 0;
per_ch->buffer_firstframe = 0;
per_ch->buffer_numframes = 0;
// attach the struct to our channel
ch->fetcher_data = (void *)per_ch;
// reset internal xmp state / syncs buffer start with frame start
qxmp_play_buffer(per_ch->playercontext, NULL, 0, 0);
}
// if the request is too large for our buffer, loop...
while (numsampleframes * f > (int)sizeof(per_ch->buffer))
{
done = sizeof(per_ch->buffer) / f;
XMP_GetSamplesFloat(ch, sfx, firstsampleframe, done, outsamplesfloat);
firstsampleframe += done;
numsampleframes -= done;
outsamplesfloat += done * sfx->format.channels;
}
// seek if the request is before the current buffer (loop back)
// seek if the request starts beyond the current buffer by at least one frame (channel was zero volume for a while)
// do not seek if the request overlaps the buffer end at all (expected behavior)
if (per_ch->buffer_firstframe > firstsampleframe || per_ch->buffer_firstframe + per_ch->buffer_numframes < firstsampleframe)
{
// we expect to decode forward from here so this will be our new buffer start
per_ch->buffer_firstframe = firstsampleframe;
per_ch->buffer_numframes = 0;
// no seeking at this time
}
// render the file to pcm as needed
if (firstsampleframe + numsampleframes > per_ch->buffer_firstframe + per_ch->buffer_numframes)
{
// first slide the buffer back, discarding any data preceding the range we care about
int offset = firstsampleframe - per_ch->buffer_firstframe;
int keeplength = per_ch->buffer_numframes - offset;
if (keeplength > 0)
memmove(per_ch->buffer, per_ch->buffer + offset * sfx->format.width * sfx->format.channels, keeplength * sfx->format.width * sfx->format.channels);
per_ch->buffer_firstframe = firstsampleframe;
per_ch->buffer_numframes -= offset;
// render as much as we can fit in the buffer
newlength = sizeof(per_ch->buffer) - per_ch->buffer_numframes * f;
done = 0;
// while (newlength > done && qxmp_play_buffer(per_ch->playercontext, (void *)((char *)per_ch->buffer + done), (int)(newlength - done), 1) == 0) // don't loop by default (TODO: fix pcm duration calculation first)
while (newlength > done && qxmp_play_buffer(per_ch->playercontext, (void *)((char *)per_ch->buffer + done), (int)(newlength - done), 0) == 0) // loop forever
{
done += (int)(newlength - done);
}
// clear the missing space if any
if (done < newlength)
{
memset(per_ch->buffer + done, 0, newlength - done);
}
// we now have more data in the buffer
per_ch->buffer_numframes += done / f;
}
// convert the sample format for the caller
buf = (short *)((char *)per_ch->buffer + (firstsampleframe - per_ch->buffer_firstframe) * f);
for (i = 0;i < len;i++)
outsamplesfloat[i] = buf[i] * (1.0f / 32768.0f);
}
/*
====================
XMP_StopChannel
====================
*/
static void XMP_StopChannel(channel_t *ch)
{
xmp_stream_perchannel_t *per_ch = (xmp_stream_perchannel_t *)ch->fetcher_data;
if (per_ch != NULL)
{
// stop the player
qxmp_end_player(per_ch->playercontext);
// free the module
qxmp_release_module(per_ch->playercontext);
// free the xmp playercontext
qxmp_free_context(per_ch->playercontext);
Mem_Free(per_ch);
}
}
/*
====================
XMP_FreeSfx
====================
*/
static void XMP_FreeSfx(sfx_t *sfx)
{
xmp_stream_persfx_t* per_sfx = (xmp_stream_persfx_t *)sfx->fetcher_data;
// free the complete file we were keeping around
Mem_Free(per_sfx->file);
// free the file information structure
Mem_Free(per_sfx);
}
static const snd_fetcher_t xmp_fetcher = { XMP_GetSamplesFloat, XMP_StopChannel, XMP_FreeSfx };
/*
===============
XMP_LoadModFile
Load an XMP module file into memory
===============
*/
qbool XMP_LoadModFile(const char *filename, sfx_t *sfx)
{
fs_offset_t filesize;
unsigned char *data;
xmp_context xc;
xmp_stream_persfx_t* per_sfx;
struct xmp_module_info mi;
#ifndef LINK_TO_LIBXMP
if (!xmp_dll)
return false;
#endif
// COMMANDLINEOPTION: Sound: -noxmp disables xmp module sound support
if (Sys_CheckParm("-noxmp"))
return false;
// Return if already loaded
if (sfx->fetcher != NULL)
return true;
// Load the file
data = FS_LoadFile(filename, snd_mempool, false, &filesize);
if (data == NULL)
return false;
// Create an xmp file context
if ((xc = qxmp_create_context()) == NULL)
{
Con_Printf("error creating a libxmp file context; while trying to load file \"%s\"\n", filename);
Mem_Free(data);
return false;
}
if (developer_loading.integer >= 2)
Con_Printf("Loading Module file (libxmp) \"%s\"\n", filename);
if (qxmp_load_module_from_memory(xc, (void *)data, (long)filesize) < 0) // Added in libxmp 4.2
{
Con_Printf("error while trying to load xmp module \"%s\"\n", filename);
qxmp_free_context(xc);
Mem_Free(data);
return false;
}
if (developer_loading.integer >= 2)
Con_Printf ("\"%s\" will be streamed\n", filename);
// keep the file around
per_sfx = (xmp_stream_persfx_t *)Mem_Alloc (snd_mempool, sizeof (*per_sfx));
per_sfx->file = data;
per_sfx->filesize = filesize;
// set dp sfx
sfx->memsize += sizeof(*per_sfx);
sfx->memsize += filesize; // total memory used (including sfx_t and fetcher data)
if (S_GetSoundRate() > XMP_MAX_SRATE)
sfx->format.speed = 48000;
else if (S_GetSoundRate() < XMP_MIN_SRATE)
sfx->format.speed = 8000;
else
sfx->format.speed = S_GetSoundRate();
sfx->format.width = S_GetSoundWidth(); // 2 = 16 bit samples
sfx->format.channels = S_GetSoundChannels();
sfx->flags |= SFXFLAG_STREAMED; // cf SFXFLAG_* defines
sfx->total_length = 1<<30; // 2147384647; // in (pcm) sample frames - they always loop (FIXME this breaks after 6 hours, we need support for a real "infinite" value!)
sfx->loopstart = sfx->total_length; // (modplug does it) in sample frames. equals total_length if not looped
sfx->fetcher_data = per_sfx;
sfx->fetcher = &xmp_fetcher;
sfx->volume_peak = 0;
qxmp_get_module_info(xc, &mi);
if (developer_loading.integer >= 2)
{
Con_Printf("Decoding module (libxmp):\n"
" Module name : %s\n"
" Module type : %s\n"
" Module length: %i patterns\n"
" Patterns : %i\n"
" Instruments : %i\n"
" Samples : %i\n"
" Channels : %i\n"
" Initial Speed: %i\n"
" Initial BPM : %i\n"
" Restart Pos. : %i\n"
" Global Volume: %i\n",
mi.mod->name, mi.mod->type,
mi.mod->len, mi.mod->pat, mi.mod->ins, mi.mod->smp, mi.mod->chn,
mi.mod->spd, mi.mod->bpm, mi.mod->rst, mi.mod->gvl
);
}
else if (developer_loading.integer == 1)
Con_Printf("Decoding module (libxmp) \"%s\" (%s)\n", mi.mod->name, mi.mod->type);
qxmp_free_context(xc);
return true;
}