-
Notifications
You must be signed in to change notification settings - Fork 1
/
context.c
343 lines (271 loc) · 10.5 KB
/
context.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
/* Author: Romain "Artefact2" Dalmaso <[email protected]> */
/* This program is free software. It comes without any warranty, to the
* extent permitted by applicable law. You can redistribute it and/or
* modify it under the terms of the Do What The Fuck You Want To Public
* License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#include "xm_internal.h"
#define OFFSET(ptr) do { \
(ptr) = (void*)((intptr_t)(ptr) + (intptr_t)(*ctxp)); \
} while(0)
int xm_create_context(xm_context_t** ctxp, const char* moddata, uint32_t rate) {
return xm_create_context_safe(ctxp, moddata, SIZE_MAX, rate);
}
int xm_create_context_safe(xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) {
size_t bytes_needed;
char* mempool;
xm_context_t* ctx;
#ifdef XM_DEFENSIVE
int ret;
#endif
#ifdef XM_DEFENSIVE
if((ret = xm_check_sanity_preload(moddata, moddata_length))) {
#ifdef XM_DEBUG
sprintf( xm_debugstr, "xm_check_sanity_preload() returned %i, module is not safe to load\n", ret );
xm_stdout( xm_debugstr );
#endif
return 1;
}
#endif
bytes_needed = xm_get_memory_needed_for_context(moddata, moddata_length);
mempool = malloc(bytes_needed);
if(mempool == NULL && bytes_needed > 0) {
/* malloc() failed, trouble ahead */
#ifdef XM_DEBUG
sprintf( xm_debugstr, "call to malloc() failed, returned %p\n", (void*)mempool );
xm_stdout( xm_debugstr );
#endif
return 2;
}
/* Initialize most of the fields to 0, 0.f, NULL or false depending on type */
memset(mempool, 0, bytes_needed);
ctx = (*ctxp = (xm_context_t*)mempool);
ctx->ctx_size = bytes_needed; /* Keep original requested size for xmconvert */
mempool += PAD_TO_WORD(sizeof(xm_context_t));
ctx->rate = rate;
mempool = xm_load_module(ctx, moddata, moddata_length, mempool);
ctx->channels = (xm_channel_context_t*)mempool;
mempool += PAD_TO_WORD(ctx->module.num_channels * sizeof(xm_channel_context_t));
ctx->global_volume = 1.f;
ctx->amplification = .25f; /* XXX: some bad modules may still clip. Find out something better. */
#ifdef XM_RAMPING
ctx->volume_ramp = (1.f / 128.f);
ctx->panning_ramp = (1.f / 128.f);
#endif
for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
xm_channel_context_t* ch = ctx->channels + i;
ch->ping = true;
ch->vibrato_waveform = XM_SINE_WAVEFORM;
ch->vibrato_waveform_retrigger = true;
ch->tremolo_waveform = XM_SINE_WAVEFORM;
ch->tremolo_waveform_retrigger = true;
ch->volume = ch->volume_envelope_volume = ch->fadeout_volume = 1.0f;
ch->panning = ch->panning_envelope_panning = .5f;
ch->actual_volume = .0f;
ch->actual_panning = .5f;
}
ctx->row_loop_count = (uint8_t*)mempool;
mempool += PAD_TO_WORD(MAX_NUM_ROWS * sizeof(uint8_t));
#ifdef XM_DEFENSIVE
if((ret = xm_check_sanity_postload(ctx))) {
#ifdef XM_DEBUG
sprintf( xm_debugstr, "xm_check_sanity_postload() returned %i, module is not safe to play\n", ret);
xm_stdout( xm_debugstr );
#endif
xm_free_context(ctx);
return 1;
}
#endif
#ifdef XM_DEBUG
sprintf( xm_debugstr, "// Module loaded. Context size is %u\n", bytes_needed );
xm_stdout( xm_debugstr );
#endif
return 0;
}
void xm_create_context_from_libxmize(xm_context_t** ctxp, const char* libxmized, uint32_t rate) {
size_t ctx_size, i, j;
/* Assume ctx_size is first member of xm_context structure */
ctx_size = *(size_t*)libxmized;
*ctxp = malloc(ctx_size);
memcpy(*ctxp, libxmized, ctx_size);
(*ctxp)->rate = rate;
/* Reverse steps of libxmize.c */
OFFSET((*ctxp)->module.patterns);
OFFSET((*ctxp)->module.instruments);
OFFSET((*ctxp)->row_loop_count);
OFFSET((*ctxp)->channels);
for(i = 0; i < (*ctxp)->module.num_patterns; ++i) {
OFFSET((*ctxp)->module.patterns[i].slots);
}
for(i = 0; i < (*ctxp)->module.num_instruments; ++i) {
OFFSET((*ctxp)->module.instruments[i].samples);
for(j = 0; j < (*ctxp)->module.instruments[i].num_samples; ++j) {
OFFSET((*ctxp)->module.instruments[i].samples[j].data8);
}
}
#ifdef XM_DEBUG
sprintf( xm_debugstr, "// Module loaded. Context size is %u\n", ctx_size );
xm_stdout( xm_debugstr );
#endif
}
void xm_create_shared_context_from_libxmize(xm_context_t** ctxp, const char* libxmized, uint32_t rate) {
size_t i, j;
const xm_context_t* in = (const void*)libxmized;
xm_context_t* out;
char* alloc;
// Calculate size of memory to allocate. This is much less than a normal context because
// much of the data (the const data) remains in the shared context.
size_t sz = PAD_TO_WORD(sizeof(xm_context_t))
+ PAD_TO_WORD(in->module.length * MAX_NUM_ROWS * sizeof(uint8_t))
+ PAD_TO_WORD(in->module.num_channels * sizeof(xm_channel_context_t))
+ PAD_TO_WORD(in->module.num_patterns * sizeof(xm_pattern_t))
+ PAD_TO_WORD(in->module.num_instruments * sizeof(xm_instrument_t))
;
const xm_instrument_t* inst = (void*)((intptr_t)in + (intptr_t)in->module.instruments);
for(i = 0; i < in->module.num_instruments; ++i) {
sz += PAD_TO_WORD(inst[i].num_samples * sizeof(xm_sample_t));
}
alloc = malloc(sz);
out = (void*)alloc;
*ctxp = out;
#ifdef XM_DEFENSIVE
if(!out) {
return;
}
#endif
#ifdef XM_DEBUG
sprintf( xm_debugstr, "// Saved %.2f%% RAM usage over original XM file format\n", sz, 100.f - 100.f * (float)sz / (float)in->ctx_size );
xm_stdout( xm_debugstr );
#endif
#ifdef XM_LIBXMIZE_DELTA_SAMPLES
#error Delta coding samples not supported
#endif
// Copy the context struct first
memset(alloc, 0, sz);
memcpy(out, in, sizeof(xm_context_t));
out->rate = rate;
alloc += PAD_TO_WORD(sizeof(xm_context_t));
out->row_loop_count = (void*)alloc;
alloc += PAD_TO_WORD(in->module.length * MAX_NUM_ROWS * sizeof(uint8_t));
out->channels = (void*)alloc;
alloc += PAD_TO_WORD(in->module.num_channels * sizeof(xm_channel_context_t));
out->module.patterns = (void*)alloc;
alloc += PAD_TO_WORD(in->module.num_patterns * sizeof(xm_pattern_t));
const xm_pattern_t* pat = (void*)((intptr_t)in + (intptr_t)in->module.patterns);
memcpy(out->module.patterns, pat, in->module.num_patterns * sizeof(xm_pattern_t));
for(i = 0; i < in->module.num_patterns; ++i) {
out->module.patterns[i].slots = (void*)((intptr_t)in + (intptr_t)pat[i].slots);
}
out->module.instruments = (void*)alloc;
alloc += PAD_TO_WORD(in->module.num_instruments * sizeof(xm_instrument_t));
memcpy(out->module.instruments, inst, in->module.num_instruments * sizeof(xm_instrument_t));
for(i = 0; i < in->module.num_instruments; ++i) {
out->module.instruments[i].samples = (void*)alloc;
alloc += PAD_TO_WORD(inst[i].num_samples * sizeof(xm_sample_t));
const xm_sample_t* s = (void*)((intptr_t)in + (intptr_t)inst[i].samples);
memcpy(out->module.instruments[i].samples, s, inst[i].num_samples * sizeof(xm_sample_t));
for(j = 0; j < inst[i].num_samples; ++j) {
out->module.instruments[i].samples[j].data8 = (void*)((intptr_t)in + (intptr_t)s[j].data8);
}
}
}
void xm_free_context(xm_context_t* context) {
free(context);
}
void xm_set_max_loop_count(xm_context_t* context, uint8_t loopcnt) {
context->max_loop_count = loopcnt;
}
uint8_t xm_get_loop_count(xm_context_t* context) {
return context->loop_count;
}
void xm_seek(xm_context_t* ctx, uint8_t pot, uint8_t row, uint16_t tick) {
ctx->current_table_index = pot;
ctx->current_row = row;
ctx->current_tick = tick;
ctx->remaining_samples_in_tick = 0;
}
bool xm_mute_channel(xm_context_t* ctx, uint16_t channel, bool mute) {
bool old = ctx->channels[channel - 1].muted;
ctx->channels[channel - 1].muted = mute;
return old;
}
bool xm_mute_instrument(xm_context_t* ctx, uint16_t instr, bool mute) {
bool old = ctx->module.instruments[instr - 1].muted;
ctx->module.instruments[instr - 1].muted = mute;
return old;
}
#if XM_STRINGS
const char* xm_get_module_name(xm_context_t* ctx) {
return ctx->module.name;
}
const char* xm_get_tracker_name(xm_context_t* ctx) {
return ctx->module.trackername;
}
#else
const char* xm_get_module_name(xm_context_t* ctx) {
return NULL;
}
const char* xm_get_tracker_name(xm_context_t* ctx) {
return NULL;
}
#endif
uint16_t xm_get_number_of_channels(xm_context_t* ctx) {
return ctx->module.num_channels;
}
uint16_t xm_get_module_length(xm_context_t* ctx) {
return ctx->module.length;
}
uint16_t xm_get_number_of_patterns(xm_context_t* ctx) {
return ctx->module.num_patterns;
}
uint16_t xm_get_number_of_rows(xm_context_t* ctx, uint16_t pattern) {
return ctx->module.patterns[pattern].num_rows;
}
uint16_t xm_get_number_of_instruments(xm_context_t* ctx) {
return ctx->module.num_instruments;
}
uint16_t xm_get_number_of_samples(xm_context_t* ctx, uint16_t instrument) {
return ctx->module.instruments[instrument - 1].num_samples;
}
void* xm_get_sample_waveform(xm_context_t* ctx, uint16_t i, uint16_t s, size_t* size, uint8_t* bits) {
*size = ctx->module.instruments[i - 1].samples[s].length;
*bits = ctx->module.instruments[i - 1].samples[s].bits;
return ctx->module.instruments[i - 1].samples[s].data8;
}
void xm_get_playing_speed(xm_context_t* ctx, uint16_t* bpm, uint16_t* tempo) {
if(bpm) *bpm = ctx->bpm;
if(tempo) *tempo = ctx->tempo;
}
void xm_get_position(xm_context_t* ctx, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples) {
if(pattern_index) *pattern_index = ctx->current_table_index;
if(pattern) *pattern = ctx->module.pattern_table[ctx->current_table_index];
if(row) *row = ctx->current_row;
if(samples) *samples = ctx->generated_samples;
}
uint64_t xm_get_latest_trigger_of_instrument(xm_context_t* ctx, uint16_t instr) {
return ctx->module.instruments[instr - 1].latest_trigger;
}
uint64_t xm_get_latest_trigger_of_sample(xm_context_t* ctx, uint16_t instr, uint16_t sample) {
return ctx->module.instruments[instr - 1].samples[sample].latest_trigger;
}
uint64_t xm_get_latest_trigger_of_channel(xm_context_t* ctx, uint16_t chn) {
return ctx->channels[chn - 1].latest_trigger;
}
bool xm_is_channel_active(xm_context_t* ctx, uint16_t chn) {
xm_channel_context_t* ch = ctx->channels + (chn - 1);
return ch->instrument != NULL && ch->sample != NULL && ch->sample_position >= 0;
}
float xm_get_frequency_of_channel(xm_context_t* ctx, uint16_t chn) {
return ctx->channels[chn - 1].frequency;
}
float xm_get_volume_of_channel(xm_context_t* ctx, uint16_t chn) {
return ctx->channels[chn - 1].actual_volume;
}
float xm_get_panning_of_channel(xm_context_t* ctx, uint16_t chn) {
return ctx->channels[chn - 1].actual_panning;
}
uint16_t xm_get_instrument_of_channel(xm_context_t* ctx, uint16_t chn) {
xm_channel_context_t* ch = ctx->channels + (chn - 1);
if(ch->instrument == NULL) return 0;
return 1 + (ch->instrument - ctx->module.instruments);
}