diff options
Diffstat (limited to 'raylib/src/external/jar_xm.h')
| -rw-r--r-- | raylib/src/external/jar_xm.h | 2471 |
1 files changed, 2471 insertions, 0 deletions
diff --git a/raylib/src/external/jar_xm.h b/raylib/src/external/jar_xm.h new file mode 100644 index 0000000..b5e80e4 --- /dev/null +++ b/raylib/src/external/jar_xm.h | |||
| @@ -0,0 +1,2471 @@ | |||
| 1 | // jar_xm.h | ||
| 2 | // | ||
| 3 | // ORIGINAL LICENSE - FOR LIBXM: | ||
| 4 | // | ||
| 5 | // Author: Romain "Artefact2" Dalmaso <artefact2@gmail.com> | ||
| 6 | // Contributor: Dan Spencer <dan@atomicpotato.net> | ||
| 7 | // Repackaged into jar_xm.h By: Joshua Adam Reisenauer <kd7tck@gmail.com> | ||
| 8 | // This program is free software. It comes without any warranty, to the | ||
| 9 | // extent permitted by applicable law. You can redistribute it and/or | ||
| 10 | // modify it under the terms of the Do What The Fuck You Want To Public | ||
| 11 | // License, Version 2, as published by Sam Hocevar. See | ||
| 12 | // http://sam.zoy.org/wtfpl/COPYING for more details. | ||
| 13 | // | ||
| 14 | // HISTORY: | ||
| 15 | // v0.1.0 2016-02-22 jar_xm.h - development by Joshua Reisenauer, MAR 2016 | ||
| 16 | // v0.2.1 2021-03-07 m4ntr0n1c: Fix clipping noise for "bad" xm's (they will always clip), avoid clip noise and just put a ceiling) | ||
| 17 | // v0.2.2 2021-03-09 m4ntr0n1c: Add complete debug solution (raylib.h must be included) | ||
| 18 | // v0.2.3 2021-03-11 m4ntr0n1c: Fix tempo, bpm and volume on song stop / start / restart / loop | ||
| 19 | // v0.2.4 2021-03-17 m4ntr0n1c: Sanitize code for readability | ||
| 20 | // v0.2.5 2021-03-22 m4ntr0n1c: Minor adjustments | ||
| 21 | // v0.2.6 2021-04-01 m4ntr0n1c: Minor fixes and optimisation | ||
| 22 | // v0.3.0 2021-04-03 m4ntr0n1c: Addition of Stereo sample support, Linear Interpolation and Ramping now addressable options in code | ||
| 23 | // v0.3.1 2021-04-04 m4ntr0n1c: Volume effects column adjustments, sample offset handling adjustments | ||
| 24 | // | ||
| 25 | // USAGE: | ||
| 26 | // | ||
| 27 | // In ONE source file, put: | ||
| 28 | // | ||
| 29 | // #define JAR_XM_IMPLEMENTATION | ||
| 30 | // #include "jar_xm.h" | ||
| 31 | // | ||
| 32 | // Other source files should just include jar_xm.h | ||
| 33 | // | ||
| 34 | // SAMPLE CODE: | ||
| 35 | // | ||
| 36 | // jar_xm_context_t *musicptr; | ||
| 37 | // float musicBuffer[48000 / 60]; | ||
| 38 | // int intro_load(void) | ||
| 39 | // { | ||
| 40 | // jar_xm_create_context_from_file(&musicptr, 48000, "Song.XM"); | ||
| 41 | // return 1; | ||
| 42 | // } | ||
| 43 | // int intro_unload(void) | ||
| 44 | // { | ||
| 45 | // jar_xm_free_context(musicptr); | ||
| 46 | // return 1; | ||
| 47 | // } | ||
| 48 | // int intro_tick(long counter) | ||
| 49 | // { | ||
| 50 | // jar_xm_generate_samples(musicptr, musicBuffer, (48000 / 60) / 2); | ||
| 51 | // if(IsKeyDown(KEY_ENTER)) | ||
| 52 | // return 1; | ||
| 53 | // return 0; | ||
| 54 | // } | ||
| 55 | // | ||
| 56 | #ifndef INCLUDE_JAR_XM_H | ||
| 57 | #define INCLUDE_JAR_XM_H | ||
| 58 | |||
| 59 | #include <stdint.h> | ||
| 60 | |||
| 61 | #define JAR_XM_DEBUG 0 | ||
| 62 | #define JAR_XM_DEFENSIVE 1 | ||
| 63 | //#define JAR_XM_RAYLIB 0 // set to 0 to disable the RayLib visualizer extension | ||
| 64 | |||
| 65 | // Allow custom memory allocators | ||
| 66 | #ifndef JARXM_MALLOC | ||
| 67 | #define JARXM_MALLOC(sz) malloc(sz) | ||
| 68 | #endif | ||
| 69 | #ifndef JARXM_FREE | ||
| 70 | #define JARXM_FREE(p) free(p) | ||
| 71 | #endif | ||
| 72 | |||
| 73 | //------------------------------------------------------------------------------- | ||
| 74 | struct jar_xm_context_s; | ||
| 75 | typedef struct jar_xm_context_s jar_xm_context_t; | ||
| 76 | |||
| 77 | #ifdef __cplusplus | ||
| 78 | extern "C" { | ||
| 79 | #endif | ||
| 80 | |||
| 81 | //** Create a XM context. | ||
| 82 | // * @param moddata the contents of the module | ||
| 83 | // * @param rate play rate in Hz, recommended value of 48000 | ||
| 84 | // * @returns 0 on success | ||
| 85 | // * @returns 1 if module data is not sane | ||
| 86 | // * @returns 2 if memory allocation failed | ||
| 87 | // * @returns 3 unable to open input file | ||
| 88 | // * @returns 4 fseek() failed | ||
| 89 | // * @returns 5 fread() failed | ||
| 90 | // * @returns 6 unkown error | ||
| 91 | // * @deprecated This function is unsafe! | ||
| 92 | // * @see jar_xm_create_context_safe() | ||
| 93 | int jar_xm_create_context_from_file(jar_xm_context_t** ctx, uint32_t rate, const char* filename); | ||
| 94 | |||
| 95 | //** Create a XM context. | ||
| 96 | // * @param moddata the contents of the module | ||
| 97 | // * @param rate play rate in Hz, recommended value of 48000 | ||
| 98 | // * @returns 0 on success | ||
| 99 | // * @returns 1 if module data is not sane | ||
| 100 | // * @returns 2 if memory allocation failed | ||
| 101 | // * @deprecated This function is unsafe! | ||
| 102 | // * @see jar_xm_create_context_safe() | ||
| 103 | int jar_xm_create_context(jar_xm_context_t** ctx, const char* moddata, uint32_t rate); | ||
| 104 | |||
| 105 | //** Create a XM context. | ||
| 106 | // * @param moddata the contents of the module | ||
| 107 | // * @param moddata_length the length of the contents of the module, in bytes | ||
| 108 | // * @param rate play rate in Hz, recommended value of 48000 | ||
| 109 | // * @returns 0 on success | ||
| 110 | // * @returns 1 if module data is not sane | ||
| 111 | // * @returns 2 if memory allocation failed | ||
| 112 | int jar_xm_create_context_safe(jar_xm_context_t** ctx, const char* moddata, size_t moddata_length, uint32_t rate); | ||
| 113 | |||
| 114 | //** Free a XM context created by jar_xm_create_context(). */ | ||
| 115 | void jar_xm_free_context(jar_xm_context_t* ctx); | ||
| 116 | |||
| 117 | //** Play the module and put the sound samples in an output buffer. | ||
| 118 | // * @param output buffer of 2*numsamples elements (A left and right value for each sample) | ||
| 119 | // * @param numsamples number of samples to generate | ||
| 120 | void jar_xm_generate_samples(jar_xm_context_t* ctx, float* output, size_t numsamples); | ||
| 121 | |||
| 122 | //** Play the module, resample from float to 16 bit, and put the sound samples in an output buffer. | ||
| 123 | // * @param output buffer of 2*numsamples elements (A left and right value for each sample) | ||
| 124 | // * @param numsamples number of samples to generate | ||
| 125 | void jar_xm_generate_samples_16bit(jar_xm_context_t* ctx, short* output, size_t numsamples) { | ||
| 126 | float* musicBuffer = JARXM_MALLOC((2*numsamples)*sizeof(float)); | ||
| 127 | jar_xm_generate_samples(ctx, musicBuffer, numsamples); | ||
| 128 | |||
| 129 | if(output){ | ||
| 130 | for(int x=0;x<2*numsamples;x++) output[x] = (musicBuffer[x] * 32767.0f); // scale sample to signed small int | ||
| 131 | } | ||
| 132 | JARXM_FREE(musicBuffer); | ||
| 133 | } | ||
| 134 | |||
| 135 | //** Play the module, resample from float to 8 bit, and put the sound samples in an output buffer. | ||
| 136 | // * @param output buffer of 2*numsamples elements (A left and right value for each sample) | ||
| 137 | // * @param numsamples number of samples to generate | ||
| 138 | void jar_xm_generate_samples_8bit(jar_xm_context_t* ctx, char* output, size_t numsamples) { | ||
| 139 | float* musicBuffer = JARXM_MALLOC((2*numsamples)*sizeof(float)); | ||
| 140 | jar_xm_generate_samples(ctx, musicBuffer, numsamples); | ||
| 141 | |||
| 142 | if(output){ | ||
| 143 | for(int x=0;x<2*numsamples;x++) output[x] = (musicBuffer[x] * 127.0f); // scale sample to signed 8 bit | ||
| 144 | } | ||
| 145 | JARXM_FREE(musicBuffer); | ||
| 146 | } | ||
| 147 | |||
| 148 | //** Set the maximum number of times a module can loop. After the specified number of loops, calls to jar_xm_generate_samples will only generate silence. You can control the current number of loops with jar_xm_get_loop_count(). | ||
| 149 | // * @param loopcnt maximum number of loops. Use 0 to loop indefinitely. | ||
| 150 | void jar_xm_set_max_loop_count(jar_xm_context_t* ctx, uint8_t loopcnt); | ||
| 151 | |||
| 152 | //** Get the loop count of the currently playing module. This value is 0 when the module is still playing, 1 when the module has looped once, etc. | ||
| 153 | uint8_t jar_xm_get_loop_count(jar_xm_context_t* ctx); | ||
| 154 | |||
| 155 | //** Mute or unmute a channel. | ||
| 156 | // * @note Channel numbers go from 1 to jar_xm_get_number_of_channels(...). | ||
| 157 | // * @return whether the channel was muted. | ||
| 158 | bool jar_xm_mute_channel(jar_xm_context_t* ctx, uint16_t, bool); | ||
| 159 | |||
| 160 | //** Mute or unmute an instrument. | ||
| 161 | // * @note Instrument numbers go from 1 to jar_xm_get_number_of_instruments(...). | ||
| 162 | // * @return whether the instrument was muted. | ||
| 163 | bool jar_xm_mute_instrument(jar_xm_context_t* ctx, uint16_t, bool); | ||
| 164 | |||
| 165 | //** Get the module name as a NUL-terminated string. | ||
| 166 | const char* jar_xm_get_module_name(jar_xm_context_t* ctx); | ||
| 167 | |||
| 168 | //** Get the tracker name as a NUL-terminated string. | ||
| 169 | const char* jar_xm_get_tracker_name(jar_xm_context_t* ctx); | ||
| 170 | |||
| 171 | //** Get the number of channels. | ||
| 172 | uint16_t jar_xm_get_number_of_channels(jar_xm_context_t* ctx); | ||
| 173 | |||
| 174 | //** Get the module length (in patterns). | ||
| 175 | uint16_t jar_xm_get_module_length(jar_xm_context_t*); | ||
| 176 | |||
| 177 | //** Get the number of patterns. | ||
| 178 | uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t* ctx); | ||
| 179 | |||
| 180 | //** Get the number of rows of a pattern. | ||
| 181 | // * @note Pattern numbers go from 0 to jar_xm_get_number_of_patterns(...)-1. | ||
| 182 | uint16_t jar_xm_get_number_of_rows(jar_xm_context_t* ctx, uint16_t); | ||
| 183 | |||
| 184 | //** Get the number of instruments. | ||
| 185 | uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t* ctx); | ||
| 186 | |||
| 187 | //** Get the number of samples of an instrument. | ||
| 188 | // * @note Instrument numbers go from 1 to jar_xm_get_number_of_instruments(...). | ||
| 189 | uint16_t jar_xm_get_number_of_samples(jar_xm_context_t* ctx, uint16_t); | ||
| 190 | |||
| 191 | //** Get the current module speed. | ||
| 192 | // * @param bpm will receive the current BPM | ||
| 193 | // * @param tempo will receive the current tempo (ticks per line) | ||
| 194 | void jar_xm_get_playing_speed(jar_xm_context_t* ctx, uint16_t* bpm, uint16_t* tempo); | ||
| 195 | |||
| 196 | //** Get the current position in the module being played. | ||
| 197 | // * @param pattern_index if not NULL, will receive the current pattern index in the POT (pattern order table) | ||
| 198 | // * @param pattern if not NULL, will receive the current pattern number | ||
| 199 | // * @param row if not NULL, will receive the current row | ||
| 200 | // * @param samples if not NULL, will receive the total number of | ||
| 201 | // * generated samples (divide by sample rate to get seconds of generated audio) | ||
| 202 | void jar_xm_get_position(jar_xm_context_t* ctx, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples); | ||
| 203 | |||
| 204 | //** Get the latest time (in number of generated samples) when a particular instrument was triggered in any channel. | ||
| 205 | // * @note Instrument numbers go from 1 to jar_xm_get_number_of_instruments(...). | ||
| 206 | uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t* ctx, uint16_t); | ||
| 207 | |||
| 208 | //** Get the latest time (in number of generated samples) when a particular sample was triggered in any channel. | ||
| 209 | // * @note Instrument numbers go from 1 to jar_xm_get_number_of_instruments(...). | ||
| 210 | // * @note Sample numbers go from 0 to jar_xm_get_nubmer_of_samples(...,instr)-1. | ||
| 211 | uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t* ctx, uint16_t instr, uint16_t sample); | ||
| 212 | |||
| 213 | //** Get the latest time (in number of generated samples) when any instrument was triggered in a given channel. | ||
| 214 | // * @note Channel numbers go from 1 to jar_xm_get_number_of_channels(...). | ||
| 215 | uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t* ctx, uint16_t); | ||
| 216 | |||
| 217 | //** Get the number of remaining samples. Divide by 2 to get the number of individual LR data samples. | ||
| 218 | // * @note This is the remaining number of samples before the loop starts module again, or halts if on last pass. | ||
| 219 | // * @note This function is very slow and should only be run once, if at all. | ||
| 220 | uint64_t jar_xm_get_remaining_samples(jar_xm_context_t* ctx); | ||
| 221 | |||
| 222 | #ifdef __cplusplus | ||
| 223 | } | ||
| 224 | #endif | ||
| 225 | //------------------------------------------------------------------------------- | ||
| 226 | |||
| 227 | #ifdef JAR_XM_IMPLEMENTATION | ||
| 228 | |||
| 229 | #include <math.h> | ||
| 230 | #include <stdio.h> | ||
| 231 | #include <stdlib.h> | ||
| 232 | #include <limits.h> | ||
| 233 | #include <string.h> | ||
| 234 | |||
| 235 | #if JAR_XM_DEBUG //JAR_XM_DEBUG defined as 0 | ||
| 236 | #include <stdio.h> | ||
| 237 | #define DEBUG(fmt, ...) do { \ | ||
| 238 | fprintf(stderr, "%s(): " fmt "\n", __func__, __VA_ARGS__); \ | ||
| 239 | fflush(stderr); \ | ||
| 240 | } while(0) | ||
| 241 | #else | ||
| 242 | #define DEBUG(...) | ||
| 243 | #endif | ||
| 244 | |||
| 245 | #if jar_xm_BIG_ENDIAN | ||
| 246 | #error "Big endian platforms are not yet supported, sorry" | ||
| 247 | /* Make sure the compiler stops, even if #error is ignored */ | ||
| 248 | extern int __fail[-1]; | ||
| 249 | #endif | ||
| 250 | |||
| 251 | /* ----- XM constants ----- */ | ||
| 252 | #define SAMPLE_NAME_LENGTH 22 | ||
| 253 | #define INSTRUMENT_NAME_LENGTH 22 | ||
| 254 | #define MODULE_NAME_LENGTH 20 | ||
| 255 | #define TRACKER_NAME_LENGTH 20 | ||
| 256 | #define PATTERN_ORDER_TABLE_LENGTH 256 | ||
| 257 | #define NUM_NOTES 96 // from 1 to 96, where 1 = C-0 | ||
| 258 | #define NUM_ENVELOPE_POINTS 12 // to be verified if 12 is the max | ||
| 259 | #define MAX_NUM_ROWS 256 | ||
| 260 | |||
| 261 | #define jar_xm_SAMPLE_RAMPING_POINTS 8 | ||
| 262 | |||
| 263 | /* ----- Data types ----- */ | ||
| 264 | |||
| 265 | enum jar_xm_waveform_type_e { | ||
| 266 | jar_xm_SINE_WAVEFORM = 0, | ||
| 267 | jar_xm_RAMP_DOWN_WAVEFORM = 1, | ||
| 268 | jar_xm_SQUARE_WAVEFORM = 2, | ||
| 269 | jar_xm_RANDOM_WAVEFORM = 3, | ||
| 270 | jar_xm_RAMP_UP_WAVEFORM = 4, | ||
| 271 | }; | ||
| 272 | typedef enum jar_xm_waveform_type_e jar_xm_waveform_type_t; | ||
| 273 | |||
| 274 | enum jar_xm_loop_type_e { | ||
| 275 | jar_xm_NO_LOOP, | ||
| 276 | jar_xm_FORWARD_LOOP, | ||
| 277 | jar_xm_PING_PONG_LOOP, | ||
| 278 | }; | ||
| 279 | typedef enum jar_xm_loop_type_e jar_xm_loop_type_t; | ||
| 280 | |||
| 281 | enum jar_xm_frequency_type_e { | ||
| 282 | jar_xm_LINEAR_FREQUENCIES, | ||
| 283 | jar_xm_AMIGA_FREQUENCIES, | ||
| 284 | }; | ||
| 285 | typedef enum jar_xm_frequency_type_e jar_xm_frequency_type_t; | ||
| 286 | |||
| 287 | struct jar_xm_envelope_point_s { | ||
| 288 | uint16_t frame; | ||
| 289 | uint16_t value; | ||
| 290 | }; | ||
| 291 | typedef struct jar_xm_envelope_point_s jar_xm_envelope_point_t; | ||
| 292 | |||
| 293 | struct jar_xm_envelope_s { | ||
| 294 | jar_xm_envelope_point_t points[NUM_ENVELOPE_POINTS]; | ||
| 295 | uint8_t num_points; | ||
| 296 | uint8_t sustain_point; | ||
| 297 | uint8_t loop_start_point; | ||
| 298 | uint8_t loop_end_point; | ||
| 299 | bool enabled; | ||
| 300 | bool sustain_enabled; | ||
| 301 | bool loop_enabled; | ||
| 302 | }; | ||
| 303 | typedef struct jar_xm_envelope_s jar_xm_envelope_t; | ||
| 304 | |||
| 305 | struct jar_xm_sample_s { | ||
| 306 | char name[SAMPLE_NAME_LENGTH + 1]; | ||
| 307 | int8_t bits; /* Either 8 or 16 */ | ||
| 308 | int8_t stereo; | ||
| 309 | uint32_t length; | ||
| 310 | uint32_t loop_start; | ||
| 311 | uint32_t loop_length; | ||
| 312 | uint32_t loop_end; | ||
| 313 | float volume; | ||
| 314 | int8_t finetune; | ||
| 315 | jar_xm_loop_type_t loop_type; | ||
| 316 | float panning; | ||
| 317 | int8_t relative_note; | ||
| 318 | uint64_t latest_trigger; | ||
| 319 | |||
| 320 | float* data; | ||
| 321 | }; | ||
| 322 | typedef struct jar_xm_sample_s jar_xm_sample_t; | ||
| 323 | |||
| 324 | struct jar_xm_instrument_s { | ||
| 325 | char name[INSTRUMENT_NAME_LENGTH + 1]; | ||
| 326 | uint16_t num_samples; | ||
| 327 | uint8_t sample_of_notes[NUM_NOTES]; | ||
| 328 | jar_xm_envelope_t volume_envelope; | ||
| 329 | jar_xm_envelope_t panning_envelope; | ||
| 330 | jar_xm_waveform_type_t vibrato_type; | ||
| 331 | uint8_t vibrato_sweep; | ||
| 332 | uint8_t vibrato_depth; | ||
| 333 | uint8_t vibrato_rate; | ||
| 334 | uint16_t volume_fadeout; | ||
| 335 | uint64_t latest_trigger; | ||
| 336 | bool muted; | ||
| 337 | |||
| 338 | jar_xm_sample_t* samples; | ||
| 339 | }; | ||
| 340 | typedef struct jar_xm_instrument_s jar_xm_instrument_t; | ||
| 341 | |||
| 342 | struct jar_xm_pattern_slot_s { | ||
| 343 | uint8_t note; /* 1-96, 97 = Key Off note */ | ||
| 344 | uint8_t instrument; /* 1-128 */ | ||
| 345 | uint8_t volume_column; | ||
| 346 | uint8_t effect_type; | ||
| 347 | uint8_t effect_param; | ||
| 348 | }; | ||
| 349 | typedef struct jar_xm_pattern_slot_s jar_xm_pattern_slot_t; | ||
| 350 | |||
| 351 | struct jar_xm_pattern_s { | ||
| 352 | uint16_t num_rows; | ||
| 353 | jar_xm_pattern_slot_t* slots; /* Array of size num_rows * num_channels */ | ||
| 354 | }; | ||
| 355 | typedef struct jar_xm_pattern_s jar_xm_pattern_t; | ||
| 356 | |||
| 357 | struct jar_xm_module_s { | ||
| 358 | char name[MODULE_NAME_LENGTH + 1]; | ||
| 359 | char trackername[TRACKER_NAME_LENGTH + 1]; | ||
| 360 | uint16_t length; | ||
| 361 | uint16_t restart_position; | ||
| 362 | uint16_t num_channels; | ||
| 363 | uint16_t num_patterns; | ||
| 364 | uint16_t num_instruments; | ||
| 365 | uint16_t linear_interpolation; | ||
| 366 | uint16_t ramping; | ||
| 367 | jar_xm_frequency_type_t frequency_type; | ||
| 368 | uint8_t pattern_table[PATTERN_ORDER_TABLE_LENGTH]; | ||
| 369 | |||
| 370 | jar_xm_pattern_t* patterns; | ||
| 371 | jar_xm_instrument_t* instruments; /* Instrument 1 has index 0, instrument 2 has index 1, etc. */ | ||
| 372 | }; | ||
| 373 | typedef struct jar_xm_module_s jar_xm_module_t; | ||
| 374 | |||
| 375 | struct jar_xm_channel_context_s { | ||
| 376 | float note; | ||
| 377 | float orig_note; /* The original note before effect modifications, as read in the pattern. */ | ||
| 378 | jar_xm_instrument_t* instrument; /* Could be NULL */ | ||
| 379 | jar_xm_sample_t* sample; /* Could be NULL */ | ||
| 380 | jar_xm_pattern_slot_t* current; | ||
| 381 | |||
| 382 | float sample_position; | ||
| 383 | float period; | ||
| 384 | float frequency; | ||
| 385 | float step; | ||
| 386 | bool ping; /* For ping-pong samples: true is -->, false is <-- */ | ||
| 387 | |||
| 388 | float volume; /* Ideally between 0 (muted) and 1 (loudest) */ | ||
| 389 | float panning; /* Between 0 (left) and 1 (right); 0.5 is centered */ | ||
| 390 | |||
| 391 | uint16_t autovibrato_ticks; | ||
| 392 | |||
| 393 | bool sustained; | ||
| 394 | float fadeout_volume; | ||
| 395 | float volume_envelope_volume; | ||
| 396 | float panning_envelope_panning; | ||
| 397 | uint16_t volume_envelope_frame_count; | ||
| 398 | uint16_t panning_envelope_frame_count; | ||
| 399 | |||
| 400 | float autovibrato_note_offset; | ||
| 401 | |||
| 402 | bool arp_in_progress; | ||
| 403 | uint8_t arp_note_offset; | ||
| 404 | uint8_t volume_slide_param; | ||
| 405 | uint8_t fine_volume_slide_param; | ||
| 406 | uint8_t global_volume_slide_param; | ||
| 407 | uint8_t panning_slide_param; | ||
| 408 | uint8_t portamento_up_param; | ||
| 409 | uint8_t portamento_down_param; | ||
| 410 | uint8_t fine_portamento_up_param; | ||
| 411 | uint8_t fine_portamento_down_param; | ||
| 412 | uint8_t extra_fine_portamento_up_param; | ||
| 413 | uint8_t extra_fine_portamento_down_param; | ||
| 414 | uint8_t tone_portamento_param; | ||
| 415 | float tone_portamento_target_period; | ||
| 416 | uint8_t multi_retrig_param; | ||
| 417 | uint8_t note_delay_param; | ||
| 418 | uint8_t pattern_loop_origin; /* Where to restart a E6y loop */ | ||
| 419 | uint8_t pattern_loop_count; /* How many loop passes have been done */ | ||
| 420 | bool vibrato_in_progress; | ||
| 421 | jar_xm_waveform_type_t vibrato_waveform; | ||
| 422 | bool vibrato_waveform_retrigger; /* True if a new note retriggers the waveform */ | ||
| 423 | uint8_t vibrato_param; | ||
| 424 | uint16_t vibrato_ticks; /* Position in the waveform */ | ||
| 425 | float vibrato_note_offset; | ||
| 426 | jar_xm_waveform_type_t tremolo_waveform; | ||
| 427 | bool tremolo_waveform_retrigger; | ||
| 428 | uint8_t tremolo_param; | ||
| 429 | uint8_t tremolo_ticks; | ||
| 430 | float tremolo_volume; | ||
| 431 | uint8_t tremor_param; | ||
| 432 | bool tremor_on; | ||
| 433 | |||
| 434 | uint64_t latest_trigger; | ||
| 435 | bool muted; | ||
| 436 | |||
| 437 | //* These values are updated at the end of each tick, to save a couple of float operations on every generated sample. | ||
| 438 | float target_panning; | ||
| 439 | float target_volume; | ||
| 440 | |||
| 441 | unsigned long frame_count; | ||
| 442 | float end_of_previous_sample_left[jar_xm_SAMPLE_RAMPING_POINTS]; | ||
| 443 | float end_of_previous_sample_right[jar_xm_SAMPLE_RAMPING_POINTS]; | ||
| 444 | float curr_left; | ||
| 445 | float curr_right; | ||
| 446 | |||
| 447 | float actual_panning; | ||
| 448 | float actual_volume; | ||
| 449 | }; | ||
| 450 | typedef struct jar_xm_channel_context_s jar_xm_channel_context_t; | ||
| 451 | |||
| 452 | struct jar_xm_context_s { | ||
| 453 | void* allocated_memory; | ||
| 454 | jar_xm_module_t module; | ||
| 455 | uint32_t rate; | ||
| 456 | |||
| 457 | uint16_t default_tempo; // Number of ticks per row | ||
| 458 | uint16_t default_bpm; | ||
| 459 | float default_global_volume; | ||
| 460 | |||
| 461 | uint16_t tempo; // Number of ticks per row | ||
| 462 | uint16_t bpm; | ||
| 463 | float global_volume; | ||
| 464 | |||
| 465 | float volume_ramp; /* How much is a channel final volume allowed to change per sample; this is used to avoid abrubt volume changes which manifest as "clicks" in the generated sound. */ | ||
| 466 | float panning_ramp; /* Same for panning. */ | ||
| 467 | |||
| 468 | uint8_t current_table_index; | ||
| 469 | uint8_t current_row; | ||
| 470 | uint16_t current_tick; /* Can go below 255, with high tempo and a pattern delay */ | ||
| 471 | float remaining_samples_in_tick; | ||
| 472 | uint64_t generated_samples; | ||
| 473 | |||
| 474 | bool position_jump; | ||
| 475 | bool pattern_break; | ||
| 476 | uint8_t jump_dest; | ||
| 477 | uint8_t jump_row; | ||
| 478 | |||
| 479 | uint16_t extra_ticks; /* Extra ticks to be played before going to the next row - Used for EEy effect */ | ||
| 480 | |||
| 481 | uint8_t* row_loop_count; /* Array of size MAX_NUM_ROWS * module_length */ | ||
| 482 | uint8_t loop_count; | ||
| 483 | uint8_t max_loop_count; | ||
| 484 | |||
| 485 | jar_xm_channel_context_t* channels; | ||
| 486 | }; | ||
| 487 | |||
| 488 | #if JAR_XM_DEFENSIVE | ||
| 489 | |||
| 490 | //** Check the module data for errors/inconsistencies. | ||
| 491 | // * @returns 0 if everything looks OK. Module should be safe to load. | ||
| 492 | int jar_xm_check_sanity_preload(const char*, size_t); | ||
| 493 | |||
| 494 | //** Check a loaded module for errors/inconsistencies. | ||
| 495 | // * @returns 0 if everything looks OK. | ||
| 496 | int jar_xm_check_sanity_postload(jar_xm_context_t*); | ||
| 497 | |||
| 498 | #endif | ||
| 499 | |||
| 500 | //** Get the number of bytes needed to store the module data in a dynamically allocated blank context. | ||
| 501 | // * Things that are dynamically allocated: | ||
| 502 | // * - sample data | ||
| 503 | // * - sample structures in instruments | ||
| 504 | // * - pattern data | ||
| 505 | // * - row loop count arrays | ||
| 506 | // * - pattern structures in module | ||
| 507 | // * - instrument structures in module | ||
| 508 | // * - channel contexts | ||
| 509 | // * - context structure itself | ||
| 510 | // * @returns 0 if everything looks OK. | ||
| 511 | size_t jar_xm_get_memory_needed_for_context(const char*, size_t); | ||
| 512 | |||
| 513 | //** Populate the context from module data. | ||
| 514 | // * @returns pointer to the memory pool | ||
| 515 | char* jar_xm_load_module(jar_xm_context_t*, const char*, size_t, char*); | ||
| 516 | |||
| 517 | int jar_xm_create_context(jar_xm_context_t** ctxp, const char* moddata, uint32_t rate) { | ||
| 518 | return jar_xm_create_context_safe(ctxp, moddata, SIZE_MAX, rate); | ||
| 519 | } | ||
| 520 | |||
| 521 | #define ALIGN(x, b) (((x) + ((b) - 1)) & ~((b) - 1)) | ||
| 522 | #define ALIGN_PTR(x, b) (void*)(((uintptr_t)(x) + ((b) - 1)) & ~((b) - 1)) | ||
| 523 | int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) { | ||
| 524 | #if JAR_XM_DEFENSIVE | ||
| 525 | int ret; | ||
| 526 | #endif | ||
| 527 | size_t bytes_needed; | ||
| 528 | char* mempool; | ||
| 529 | jar_xm_context_t* ctx; | ||
| 530 | |||
| 531 | #if JAR_XM_DEFENSIVE | ||
| 532 | if((ret = jar_xm_check_sanity_preload(moddata, moddata_length))) { | ||
| 533 | DEBUG("jar_xm_check_sanity_preload() returned %i, module is not safe to load", ret); | ||
| 534 | return 1; | ||
| 535 | } | ||
| 536 | #endif | ||
| 537 | |||
| 538 | bytes_needed = jar_xm_get_memory_needed_for_context(moddata, moddata_length); | ||
| 539 | mempool = JARXM_MALLOC(bytes_needed); | ||
| 540 | if(mempool == NULL && bytes_needed > 0) { /* JARXM_MALLOC() failed, trouble ahead */ | ||
| 541 | DEBUG("call to JARXM_MALLOC() failed, returned %p", (void*)mempool); | ||
| 542 | return 2; | ||
| 543 | } | ||
| 544 | |||
| 545 | /* Initialize most of the fields to 0, 0.f, NULL or false depending on type */ | ||
| 546 | memset(mempool, 0, bytes_needed); | ||
| 547 | |||
| 548 | ctx = (*ctxp = (jar_xm_context_t *)mempool); | ||
| 549 | ctx->allocated_memory = mempool; /* Keep original pointer for JARXM_FREE() */ | ||
| 550 | mempool += sizeof(jar_xm_context_t); | ||
| 551 | |||
| 552 | ctx->rate = rate; | ||
| 553 | mempool = jar_xm_load_module(ctx, moddata, moddata_length, mempool); | ||
| 554 | mempool = ALIGN_PTR(mempool, 16); | ||
| 555 | |||
| 556 | ctx->channels = (jar_xm_channel_context_t*)mempool; | ||
| 557 | mempool += ctx->module.num_channels * sizeof(jar_xm_channel_context_t); | ||
| 558 | mempool = ALIGN_PTR(mempool, 16); | ||
| 559 | |||
| 560 | ctx->default_global_volume = 1.f; | ||
| 561 | ctx->global_volume = ctx->default_global_volume; | ||
| 562 | |||
| 563 | ctx->volume_ramp = (1.f / 128.f); | ||
| 564 | ctx->panning_ramp = (1.f / 128.f); | ||
| 565 | |||
| 566 | for(uint8_t i = 0; i < ctx->module.num_channels; ++i) { | ||
| 567 | jar_xm_channel_context_t *ch = ctx->channels + i; | ||
| 568 | ch->ping = true; | ||
| 569 | ch->vibrato_waveform = jar_xm_SINE_WAVEFORM; | ||
| 570 | ch->vibrato_waveform_retrigger = true; | ||
| 571 | ch->tremolo_waveform = jar_xm_SINE_WAVEFORM; | ||
| 572 | ch->tremolo_waveform_retrigger = true; | ||
| 573 | ch->volume = ch->volume_envelope_volume = ch->fadeout_volume = 1.0f; | ||
| 574 | ch->panning = ch->panning_envelope_panning = .5f; | ||
| 575 | ch->actual_volume = .0f; | ||
| 576 | ch->actual_panning = .5f; | ||
| 577 | } | ||
| 578 | |||
| 579 | mempool = ALIGN_PTR(mempool, 16); | ||
| 580 | ctx->row_loop_count = (uint8_t *)mempool; | ||
| 581 | mempool += MAX_NUM_ROWS * sizeof(uint8_t); | ||
| 582 | |||
| 583 | #if JAR_XM_DEFENSIVE | ||
| 584 | if((ret = jar_xm_check_sanity_postload(ctx))) { DEBUG("jar_xm_check_sanity_postload() returned %i, module is not safe to play", ret); | ||
| 585 | jar_xm_free_context(ctx); | ||
| 586 | return 1; | ||
| 587 | } | ||
| 588 | #endif | ||
| 589 | |||
| 590 | return 0; | ||
| 591 | } | ||
| 592 | |||
| 593 | void jar_xm_free_context(jar_xm_context_t *ctx) { | ||
| 594 | if (ctx != NULL) { JARXM_FREE(ctx->allocated_memory); } | ||
| 595 | } | ||
| 596 | |||
| 597 | void jar_xm_set_max_loop_count(jar_xm_context_t *ctx, uint8_t loopcnt) { | ||
| 598 | ctx->max_loop_count = loopcnt; | ||
| 599 | } | ||
| 600 | |||
| 601 | uint8_t jar_xm_get_loop_count(jar_xm_context_t *ctx) { | ||
| 602 | return ctx->loop_count; | ||
| 603 | } | ||
| 604 | |||
| 605 | bool jar_xm_mute_channel(jar_xm_context_t *ctx, uint16_t channel, bool mute) { | ||
| 606 | bool old = ctx->channels[channel - 1].muted; | ||
| 607 | ctx->channels[channel - 1].muted = mute; | ||
| 608 | return old; | ||
| 609 | } | ||
| 610 | |||
| 611 | bool jar_xm_mute_instrument(jar_xm_context_t *ctx, uint16_t instr, bool mute) { | ||
| 612 | bool old = ctx->module.instruments[instr - 1].muted; | ||
| 613 | ctx->module.instruments[instr - 1].muted = mute; | ||
| 614 | return old; | ||
| 615 | } | ||
| 616 | |||
| 617 | const char* jar_xm_get_module_name(jar_xm_context_t *ctx) { | ||
| 618 | return ctx->module.name; | ||
| 619 | } | ||
| 620 | |||
| 621 | const char* jar_xm_get_tracker_name(jar_xm_context_t *ctx) { | ||
| 622 | return ctx->module.trackername; | ||
| 623 | } | ||
| 624 | |||
| 625 | uint16_t jar_xm_get_number_of_channels(jar_xm_context_t *ctx) { | ||
| 626 | return ctx->module.num_channels; | ||
| 627 | } | ||
| 628 | |||
| 629 | uint16_t jar_xm_get_module_length(jar_xm_context_t *ctx) { | ||
| 630 | return ctx->module.length; | ||
| 631 | } | ||
| 632 | |||
| 633 | uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t *ctx) { | ||
| 634 | return ctx->module.num_patterns; | ||
| 635 | } | ||
| 636 | |||
| 637 | uint16_t jar_xm_get_number_of_rows(jar_xm_context_t *ctx, uint16_t pattern) { | ||
| 638 | return ctx->module.patterns[pattern].num_rows; | ||
| 639 | } | ||
| 640 | |||
| 641 | uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t *ctx) { | ||
| 642 | return ctx->module.num_instruments; | ||
| 643 | } | ||
| 644 | |||
| 645 | uint16_t jar_xm_get_number_of_samples(jar_xm_context_t *ctx, uint16_t instrument) { | ||
| 646 | return ctx->module.instruments[instrument - 1].num_samples; | ||
| 647 | } | ||
| 648 | |||
| 649 | void jar_xm_get_playing_speed(jar_xm_context_t *ctx, uint16_t *bpm, uint16_t *tempo) { | ||
| 650 | if(bpm) *bpm = ctx->bpm; | ||
| 651 | if(tempo) *tempo = ctx->tempo; | ||
| 652 | } | ||
| 653 | |||
| 654 | void jar_xm_get_position(jar_xm_context_t *ctx, uint8_t *pattern_index, uint8_t *pattern, uint8_t *row, uint64_t *samples) { | ||
| 655 | if(pattern_index) *pattern_index = ctx->current_table_index; | ||
| 656 | if(pattern) *pattern = ctx->module.pattern_table[ctx->current_table_index]; | ||
| 657 | if(row) *row = ctx->current_row; | ||
| 658 | if(samples) *samples = ctx->generated_samples; | ||
| 659 | } | ||
| 660 | |||
| 661 | uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t *ctx, uint16_t instr) { | ||
| 662 | return ctx->module.instruments[instr - 1].latest_trigger; | ||
| 663 | } | ||
| 664 | |||
| 665 | uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t *ctx, uint16_t instr, uint16_t sample) { | ||
| 666 | return ctx->module.instruments[instr - 1].samples[sample].latest_trigger; | ||
| 667 | } | ||
| 668 | |||
| 669 | uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t *ctx, uint16_t chn) { | ||
| 670 | return ctx->channels[chn - 1].latest_trigger; | ||
| 671 | } | ||
| 672 | |||
| 673 | //* .xm files are little-endian. (XXX: Are they really?) | ||
| 674 | |||
| 675 | //* Bound reader macros. | ||
| 676 | //* If we attempt to read the buffer out-of-bounds, pretend that the buffer is infinitely padded with zeroes. | ||
| 677 | #define READ_U8(offset) (((offset) < moddata_length) ? (*(uint8_t*)(moddata + (offset))) : 0) | ||
| 678 | #define READ_U16(offset) ((uint16_t)READ_U8(offset) | ((uint16_t)READ_U8((offset) + 1) << 8)) | ||
| 679 | #define READ_U32(offset) ((uint32_t)READ_U16(offset) | ((uint32_t)READ_U16((offset) + 2) << 16)) | ||
| 680 | #define READ_MEMCPY(ptr, offset, length) memcpy_pad(ptr, length, moddata, moddata_length, offset) | ||
| 681 | |||
| 682 | static void memcpy_pad(void *dst, size_t dst_len, const void *src, size_t src_len, size_t offset) { | ||
| 683 | uint8_t *dst_c = dst; | ||
| 684 | const uint8_t *src_c = src; | ||
| 685 | |||
| 686 | /* how many bytes can be copied without overrunning `src` */ | ||
| 687 | size_t copy_bytes = (src_len >= offset) ? (src_len - offset) : 0; | ||
| 688 | copy_bytes = copy_bytes > dst_len ? dst_len : copy_bytes; | ||
| 689 | |||
| 690 | memcpy(dst_c, src_c + offset, copy_bytes); | ||
| 691 | /* padded bytes */ | ||
| 692 | memset(dst_c + copy_bytes, 0, dst_len - copy_bytes); | ||
| 693 | } | ||
| 694 | |||
| 695 | #if JAR_XM_DEFENSIVE | ||
| 696 | |||
| 697 | int jar_xm_check_sanity_preload(const char* module, size_t module_length) { | ||
| 698 | if(module_length < 60) { return 4; } | ||
| 699 | if(memcmp("Extended Module: ", module, 17) != 0) { return 1; } | ||
| 700 | if(module[37] != 0x1A) { return 2; } | ||
| 701 | if(module[59] != 0x01 || module[58] != 0x04) { return 3; } /* Not XM 1.04 */ | ||
| 702 | return 0; | ||
| 703 | } | ||
| 704 | |||
| 705 | int jar_xm_check_sanity_postload(jar_xm_context_t* ctx) { | ||
| 706 | /* Check the POT */ | ||
| 707 | for(uint8_t i = 0; i < ctx->module.length; ++i) { | ||
| 708 | if(ctx->module.pattern_table[i] >= ctx->module.num_patterns) { | ||
| 709 | if(i+1 == ctx->module.length && ctx->module.length > 1) { | ||
| 710 | DEBUG("trimming invalid POT at pos %X", i); | ||
| 711 | --ctx->module.length; | ||
| 712 | } else { | ||
| 713 | DEBUG("module has invalid POT, pos %X references nonexistent pattern %X", i, ctx->module.pattern_table[i]); | ||
| 714 | return 1; | ||
| 715 | } | ||
| 716 | } | ||
| 717 | } | ||
| 718 | |||
| 719 | return 0; | ||
| 720 | } | ||
| 721 | |||
| 722 | #endif | ||
| 723 | |||
| 724 | size_t jar_xm_get_memory_needed_for_context(const char* moddata, size_t moddata_length) { | ||
| 725 | size_t memory_needed = 0; | ||
| 726 | size_t offset = 60; /* 60 = Skip the first header */ | ||
| 727 | uint16_t num_channels; | ||
| 728 | uint16_t num_patterns; | ||
| 729 | uint16_t num_instruments; | ||
| 730 | |||
| 731 | /* Read the module header */ | ||
| 732 | num_channels = READ_U16(offset + 8); | ||
| 733 | num_patterns = READ_U16(offset + 10); | ||
| 734 | memory_needed += num_patterns * sizeof(jar_xm_pattern_t); | ||
| 735 | memory_needed = ALIGN(memory_needed, 16); | ||
| 736 | num_instruments = READ_U16(offset + 12); | ||
| 737 | memory_needed += num_instruments * sizeof(jar_xm_instrument_t); | ||
| 738 | memory_needed = ALIGN(memory_needed, 16); | ||
| 739 | memory_needed += MAX_NUM_ROWS * READ_U16(offset + 4) * sizeof(uint8_t); /* Module length */ | ||
| 740 | |||
| 741 | offset += READ_U32(offset); /* Header size */ | ||
| 742 | |||
| 743 | /* Read pattern headers */ | ||
| 744 | for(uint16_t i = 0; i < num_patterns; ++i) { | ||
| 745 | uint16_t num_rows; | ||
| 746 | num_rows = READ_U16(offset + 5); | ||
| 747 | memory_needed += num_rows * num_channels * sizeof(jar_xm_pattern_slot_t); | ||
| 748 | offset += READ_U32(offset) + READ_U16(offset + 7); /* Pattern header length + packed pattern data size */ | ||
| 749 | } | ||
| 750 | memory_needed = ALIGN(memory_needed, 16); | ||
| 751 | |||
| 752 | /* Read instrument headers */ | ||
| 753 | for(uint16_t i = 0; i < num_instruments; ++i) { | ||
| 754 | uint16_t num_samples; | ||
| 755 | uint32_t sample_header_size = 0; | ||
| 756 | uint32_t sample_size_aggregate = 0; | ||
| 757 | num_samples = READ_U16(offset + 27); | ||
| 758 | memory_needed += num_samples * sizeof(jar_xm_sample_t); | ||
| 759 | if(num_samples > 0) { sample_header_size = READ_U32(offset + 29); } | ||
| 760 | |||
| 761 | offset += READ_U32(offset); /* Instrument header size */ | ||
| 762 | for(uint16_t j = 0; j < num_samples; ++j) { | ||
| 763 | uint32_t sample_size; | ||
| 764 | uint8_t flags; | ||
| 765 | sample_size = READ_U32(offset); | ||
| 766 | flags = READ_U8(offset + 14); | ||
| 767 | sample_size_aggregate += sample_size; | ||
| 768 | |||
| 769 | if(flags & (1 << 4)) { /* 16 bit sample */ | ||
| 770 | memory_needed += sample_size * (sizeof(float) >> 1); | ||
| 771 | } else { /* 8 bit sample */ | ||
| 772 | memory_needed += sample_size * sizeof(float); | ||
| 773 | } | ||
| 774 | offset += sample_header_size; | ||
| 775 | } | ||
| 776 | offset += sample_size_aggregate; | ||
| 777 | } | ||
| 778 | |||
| 779 | memory_needed += num_channels * sizeof(jar_xm_channel_context_t); | ||
| 780 | memory_needed += sizeof(jar_xm_context_t); | ||
| 781 | return memory_needed; | ||
| 782 | } | ||
| 783 | |||
| 784 | char* jar_xm_load_module(jar_xm_context_t* ctx, const char* moddata, size_t moddata_length, char* mempool) { | ||
| 785 | size_t offset = 0; | ||
| 786 | jar_xm_module_t* mod = &(ctx->module); | ||
| 787 | |||
| 788 | /* Read XM header */ | ||
| 789 | READ_MEMCPY(mod->name, offset + 17, MODULE_NAME_LENGTH); | ||
| 790 | READ_MEMCPY(mod->trackername, offset + 38, TRACKER_NAME_LENGTH); | ||
| 791 | offset += 60; | ||
| 792 | |||
| 793 | /* Read module header */ | ||
| 794 | uint32_t header_size = READ_U32(offset); | ||
| 795 | mod->length = READ_U16(offset + 4); | ||
| 796 | mod->restart_position = READ_U16(offset + 6); | ||
| 797 | mod->num_channels = READ_U16(offset + 8); | ||
| 798 | mod->num_patterns = READ_U16(offset + 10); | ||
| 799 | mod->num_instruments = READ_U16(offset + 12); | ||
| 800 | mod->patterns = (jar_xm_pattern_t*)mempool; | ||
| 801 | mod->linear_interpolation = 1; // Linear interpolation can be set after loading | ||
| 802 | mod->ramping = 1; // ramping can be set after loading | ||
| 803 | mempool += mod->num_patterns * sizeof(jar_xm_pattern_t); | ||
| 804 | mempool = ALIGN_PTR(mempool, 16); | ||
| 805 | mod->instruments = (jar_xm_instrument_t*)mempool; | ||
| 806 | mempool += mod->num_instruments * sizeof(jar_xm_instrument_t); | ||
| 807 | mempool = ALIGN_PTR(mempool, 16); | ||
| 808 | uint16_t flags = READ_U32(offset + 14); | ||
| 809 | mod->frequency_type = (flags & (1 << 0)) ? jar_xm_LINEAR_FREQUENCIES : jar_xm_AMIGA_FREQUENCIES; | ||
| 810 | ctx->default_tempo = READ_U16(offset + 16); | ||
| 811 | ctx->default_bpm = READ_U16(offset + 18); | ||
| 812 | ctx->tempo =ctx->default_tempo; | ||
| 813 | ctx->bpm = ctx->default_bpm; | ||
| 814 | |||
| 815 | READ_MEMCPY(mod->pattern_table, offset + 20, PATTERN_ORDER_TABLE_LENGTH); | ||
| 816 | offset += header_size; | ||
| 817 | |||
| 818 | /* Read patterns */ | ||
| 819 | for(uint16_t i = 0; i < mod->num_patterns; ++i) { | ||
| 820 | uint16_t packed_patterndata_size = READ_U16(offset + 7); | ||
| 821 | jar_xm_pattern_t* pat = mod->patterns + i; | ||
| 822 | pat->num_rows = READ_U16(offset + 5); | ||
| 823 | pat->slots = (jar_xm_pattern_slot_t*)mempool; | ||
| 824 | mempool += mod->num_channels * pat->num_rows * sizeof(jar_xm_pattern_slot_t); | ||
| 825 | offset += READ_U32(offset); /* Pattern header length */ | ||
| 826 | |||
| 827 | if(packed_patterndata_size == 0) { /* No pattern data is present */ | ||
| 828 | memset(pat->slots, 0, sizeof(jar_xm_pattern_slot_t) * pat->num_rows * mod->num_channels); | ||
| 829 | } else { | ||
| 830 | /* This isn't your typical for loop */ | ||
| 831 | for(uint16_t j = 0, k = 0; j < packed_patterndata_size; ++k) { | ||
| 832 | uint8_t note = READ_U8(offset + j); | ||
| 833 | jar_xm_pattern_slot_t* slot = pat->slots + k; | ||
| 834 | if(note & (1 << 7)) { | ||
| 835 | /* MSB is set, this is a compressed packet */ | ||
| 836 | ++j; | ||
| 837 | if(note & (1 << 0)) { /* Note follows */ | ||
| 838 | slot->note = READ_U8(offset + j); | ||
| 839 | ++j; | ||
| 840 | } else { | ||
| 841 | slot->note = 0; | ||
| 842 | } | ||
| 843 | if(note & (1 << 1)) { /* Instrument follows */ | ||
| 844 | slot->instrument = READ_U8(offset + j); | ||
| 845 | ++j; | ||
| 846 | } else { | ||
| 847 | slot->instrument = 0; | ||
| 848 | } | ||
| 849 | if(note & (1 << 2)) { /* Volume column follows */ | ||
| 850 | slot->volume_column = READ_U8(offset + j); | ||
| 851 | ++j; | ||
| 852 | } else { | ||
| 853 | slot->volume_column = 0; | ||
| 854 | } | ||
| 855 | if(note & (1 << 3)) { /* Effect follows */ | ||
| 856 | slot->effect_type = READ_U8(offset + j); | ||
| 857 | ++j; | ||
| 858 | } else { | ||
| 859 | slot->effect_type = 0; | ||
| 860 | } | ||
| 861 | if(note & (1 << 4)) { /* Effect parameter follows */ | ||
| 862 | slot->effect_param = READ_U8(offset + j); | ||
| 863 | ++j; | ||
| 864 | } else { | ||
| 865 | slot->effect_param = 0; | ||
| 866 | } | ||
| 867 | } else { /* Uncompressed packet */ | ||
| 868 | slot->note = note; | ||
| 869 | slot->instrument = READ_U8(offset + j + 1); | ||
| 870 | slot->volume_column = READ_U8(offset + j + 2); | ||
| 871 | slot->effect_type = READ_U8(offset + j + 3); | ||
| 872 | slot->effect_param = READ_U8(offset + j + 4); | ||
| 873 | j += 5; | ||
| 874 | } | ||
| 875 | } | ||
| 876 | } | ||
| 877 | |||
| 878 | offset += packed_patterndata_size; | ||
| 879 | } | ||
| 880 | mempool = ALIGN_PTR(mempool, 16); | ||
| 881 | |||
| 882 | /* Read instruments */ | ||
| 883 | for(uint16_t i = 0; i < ctx->module.num_instruments; ++i) { | ||
| 884 | uint32_t sample_header_size = 0; | ||
| 885 | jar_xm_instrument_t* instr = mod->instruments + i; | ||
| 886 | |||
| 887 | READ_MEMCPY(instr->name, offset + 4, INSTRUMENT_NAME_LENGTH); | ||
| 888 | instr->num_samples = READ_U16(offset + 27); | ||
| 889 | |||
| 890 | if(instr->num_samples > 0) { | ||
| 891 | /* Read extra header properties */ | ||
| 892 | sample_header_size = READ_U32(offset + 29); | ||
| 893 | READ_MEMCPY(instr->sample_of_notes, offset + 33, NUM_NOTES); | ||
| 894 | |||
| 895 | instr->volume_envelope.num_points = READ_U8(offset + 225); | ||
| 896 | instr->panning_envelope.num_points = READ_U8(offset + 226); | ||
| 897 | |||
| 898 | for(uint8_t j = 0; j < instr->volume_envelope.num_points; ++j) { | ||
| 899 | instr->volume_envelope.points[j].frame = READ_U16(offset + 129 + 4 * j); | ||
| 900 | instr->volume_envelope.points[j].value = READ_U16(offset + 129 + 4 * j + 2); | ||
| 901 | } | ||
| 902 | |||
| 903 | for(uint8_t j = 0; j < instr->panning_envelope.num_points; ++j) { | ||
| 904 | instr->panning_envelope.points[j].frame = READ_U16(offset + 177 + 4 * j); | ||
| 905 | instr->panning_envelope.points[j].value = READ_U16(offset + 177 + 4 * j + 2); | ||
| 906 | } | ||
| 907 | |||
| 908 | instr->volume_envelope.sustain_point = READ_U8(offset + 227); | ||
| 909 | instr->volume_envelope.loop_start_point = READ_U8(offset + 228); | ||
| 910 | instr->volume_envelope.loop_end_point = READ_U8(offset + 229); | ||
| 911 | instr->panning_envelope.sustain_point = READ_U8(offset + 230); | ||
| 912 | instr->panning_envelope.loop_start_point = READ_U8(offset + 231); | ||
| 913 | instr->panning_envelope.loop_end_point = READ_U8(offset + 232); | ||
| 914 | |||
| 915 | uint8_t flags = READ_U8(offset + 233); | ||
| 916 | instr->volume_envelope.enabled = flags & (1 << 0); | ||
| 917 | instr->volume_envelope.sustain_enabled = flags & (1 << 1); | ||
| 918 | instr->volume_envelope.loop_enabled = flags & (1 << 2); | ||
| 919 | |||
| 920 | flags = READ_U8(offset + 234); | ||
| 921 | instr->panning_envelope.enabled = flags & (1 << 0); | ||
| 922 | instr->panning_envelope.sustain_enabled = flags & (1 << 1); | ||
| 923 | instr->panning_envelope.loop_enabled = flags & (1 << 2); | ||
| 924 | instr->vibrato_type = READ_U8(offset + 235); | ||
| 925 | if(instr->vibrato_type == 2) { | ||
| 926 | instr->vibrato_type = 1; | ||
| 927 | } else if(instr->vibrato_type == 1) { | ||
| 928 | instr->vibrato_type = 2; | ||
| 929 | } | ||
| 930 | instr->vibrato_sweep = READ_U8(offset + 236); | ||
| 931 | instr->vibrato_depth = READ_U8(offset + 237); | ||
| 932 | instr->vibrato_rate = READ_U8(offset + 238); | ||
| 933 | instr->volume_fadeout = READ_U16(offset + 239); | ||
| 934 | instr->samples = (jar_xm_sample_t*)mempool; | ||
| 935 | mempool += instr->num_samples * sizeof(jar_xm_sample_t); | ||
| 936 | } else { | ||
| 937 | instr->samples = NULL; | ||
| 938 | } | ||
| 939 | |||
| 940 | /* Instrument header size */ | ||
| 941 | offset += READ_U32(offset); | ||
| 942 | |||
| 943 | for(int j = 0; j < instr->num_samples; ++j) { | ||
| 944 | /* Read sample header */ | ||
| 945 | jar_xm_sample_t* sample = instr->samples + j; | ||
| 946 | |||
| 947 | sample->length = READ_U32(offset); | ||
| 948 | sample->loop_start = READ_U32(offset + 4); | ||
| 949 | sample->loop_length = READ_U32(offset + 8); | ||
| 950 | sample->loop_end = sample->loop_start + sample->loop_length; | ||
| 951 | sample->volume = (float)(READ_U8(offset + 12) << 2) / 256.f; | ||
| 952 | if (sample->volume > 1.0f) {sample->volume = 1.f;}; | ||
| 953 | sample->finetune = (int8_t)READ_U8(offset + 13); | ||
| 954 | |||
| 955 | uint8_t flags = READ_U8(offset + 14); | ||
| 956 | switch (flags & 3) { | ||
| 957 | case 2: | ||
| 958 | case 3: | ||
| 959 | sample->loop_type = jar_xm_PING_PONG_LOOP; | ||
| 960 | case 1: | ||
| 961 | sample->loop_type = jar_xm_FORWARD_LOOP; | ||
| 962 | break; | ||
| 963 | default: | ||
| 964 | sample->loop_type = jar_xm_NO_LOOP; | ||
| 965 | break; | ||
| 966 | }; | ||
| 967 | sample->bits = (flags & 0x10) ? 16 : 8; | ||
| 968 | sample->stereo = (flags & 0x20) ? 1 : 0; | ||
| 969 | sample->panning = (float)READ_U8(offset + 15) / 255.f; | ||
| 970 | sample->relative_note = (int8_t)READ_U8(offset + 16); | ||
| 971 | READ_MEMCPY(sample->name, 18, SAMPLE_NAME_LENGTH); | ||
| 972 | sample->data = (float*)mempool; | ||
| 973 | if(sample->bits == 16) { | ||
| 974 | /* 16 bit sample */ | ||
| 975 | mempool += sample->length * (sizeof(float) >> 1); | ||
| 976 | sample->loop_start >>= 1; | ||
| 977 | sample->loop_length >>= 1; | ||
| 978 | sample->loop_end >>= 1; | ||
| 979 | sample->length >>= 1; | ||
| 980 | } else { | ||
| 981 | /* 8 bit sample */ | ||
| 982 | mempool += sample->length * sizeof(float); | ||
| 983 | } | ||
| 984 | // Adjust loop points to reflect half of the reported length (stereo) | ||
| 985 | if (sample->stereo && sample->loop_type != jar_xm_NO_LOOP) { | ||
| 986 | div_t lstart = div(READ_U32(offset + 4), 2); | ||
| 987 | sample->loop_start = lstart.quot; | ||
| 988 | div_t llength = div(READ_U32(offset + 8), 2); | ||
| 989 | sample->loop_length = llength.quot; | ||
| 990 | sample->loop_end = sample->loop_start + sample->loop_length; | ||
| 991 | }; | ||
| 992 | |||
| 993 | offset += sample_header_size; | ||
| 994 | } | ||
| 995 | |||
| 996 | // Read all samples and convert them to float values | ||
| 997 | for(int j = 0; j < instr->num_samples; ++j) { | ||
| 998 | /* Read sample data */ | ||
| 999 | jar_xm_sample_t* sample = instr->samples + j; | ||
| 1000 | int length = sample->length; | ||
| 1001 | if (sample->stereo) { | ||
| 1002 | // Since it is stereo, we cut the sample in half (treated as single channel) | ||
| 1003 | div_t result = div(sample->length, 2); | ||
| 1004 | if(sample->bits == 16) { | ||
| 1005 | int16_t v = 0; | ||
| 1006 | for(int k = 0; k < length; ++k) { | ||
| 1007 | if (k == result.quot) { v = 0;}; | ||
| 1008 | v = v + (int16_t)READ_U16(offset + (k << 1)); | ||
| 1009 | sample->data[k] = (float) v / 32768.f ;//* sign; | ||
| 1010 | if(sample->data[k] < -1.0) {sample->data[k] = -1.0;} else if(sample->data[k] > 1.0) {sample->data[k] = 1.0;}; | ||
| 1011 | } | ||
| 1012 | offset += sample->length << 1; | ||
| 1013 | } else { | ||
| 1014 | int8_t v = 0; | ||
| 1015 | for(int k = 0; k < length; ++k) { | ||
| 1016 | if (k == result.quot) { v = 0;}; | ||
| 1017 | v = v + (int8_t)READ_U8(offset + k); | ||
| 1018 | sample->data[k] = (float)v / 128.f ;//* sign; | ||
| 1019 | if(sample->data[k] < -1.0) {sample->data[k] = -1.0;} else if(sample->data[k] > 1.0) {sample->data[k] = 1.0;}; | ||
| 1020 | } | ||
| 1021 | offset += sample->length; | ||
| 1022 | }; | ||
| 1023 | sample->length = result.quot; | ||
| 1024 | } else { | ||
| 1025 | if(sample->bits == 16) { | ||
| 1026 | int16_t v = 0; | ||
| 1027 | for(int k = 0; k < length; ++k) { | ||
| 1028 | v = v + (int16_t)READ_U16(offset + (k << 1)); | ||
| 1029 | sample->data[k] = (float) v / 32768.f ;//* sign; | ||
| 1030 | if(sample->data[k] < -1.0) {sample->data[k] = -1.0;} else if(sample->data[k] > 1.0) {sample->data[k] = 1.0;}; | ||
| 1031 | } | ||
| 1032 | offset += sample->length << 1; | ||
| 1033 | } else { | ||
| 1034 | int8_t v = 0; | ||
| 1035 | for(int k = 0; k < length; ++k) { | ||
| 1036 | v = v + (int8_t)READ_U8(offset + k); | ||
| 1037 | sample->data[k] = (float)v / 128.f ;//* sign; | ||
| 1038 | if(sample->data[k] < -1.0) {sample->data[k] = -1.0;} else if(sample->data[k] > 1.0) {sample->data[k] = 1.0;}; | ||
| 1039 | } | ||
| 1040 | offset += sample->length; | ||
| 1041 | } | ||
| 1042 | } | ||
| 1043 | }; | ||
| 1044 | }; | ||
| 1045 | return mempool; | ||
| 1046 | }; | ||
| 1047 | |||
| 1048 | //------------------------------------------------------------------------------- | ||
| 1049 | //THE FOLLOWING IS FOR PLAYING | ||
| 1050 | static float jar_xm_waveform(jar_xm_waveform_type_t, uint8_t); | ||
| 1051 | static void jar_xm_autovibrato(jar_xm_context_t*, jar_xm_channel_context_t*); | ||
| 1052 | static void jar_xm_vibrato(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t); | ||
| 1053 | static void jar_xm_tremolo(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t); | ||
| 1054 | static void jar_xm_arpeggio(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t); | ||
| 1055 | static void jar_xm_tone_portamento(jar_xm_context_t*, jar_xm_channel_context_t*); | ||
| 1056 | static void jar_xm_pitch_slide(jar_xm_context_t*, jar_xm_channel_context_t*, float); | ||
| 1057 | static void jar_xm_panning_slide(jar_xm_channel_context_t*, uint8_t); | ||
| 1058 | static void jar_xm_volume_slide(jar_xm_channel_context_t*, uint8_t); | ||
| 1059 | |||
| 1060 | static float jar_xm_envelope_lerp(jar_xm_envelope_point_t*, jar_xm_envelope_point_t*, uint16_t); | ||
| 1061 | static void jar_xm_envelope_tick(jar_xm_channel_context_t*, jar_xm_envelope_t*, uint16_t*, float*); | ||
| 1062 | static void jar_xm_envelopes(jar_xm_channel_context_t*); | ||
| 1063 | |||
| 1064 | static float jar_xm_linear_period(float); | ||
| 1065 | static float jar_xm_linear_frequency(float); | ||
| 1066 | static float jar_xm_amiga_period(float); | ||
| 1067 | static float jar_xm_amiga_frequency(float); | ||
| 1068 | static float jar_xm_period(jar_xm_context_t*, float); | ||
| 1069 | static float jar_xm_frequency(jar_xm_context_t*, float, float); | ||
| 1070 | static void jar_xm_update_frequency(jar_xm_context_t*, jar_xm_channel_context_t*); | ||
| 1071 | |||
| 1072 | static void jar_xm_handle_note_and_instrument(jar_xm_context_t*, jar_xm_channel_context_t*, jar_xm_pattern_slot_t*); | ||
| 1073 | static void jar_xm_trigger_note(jar_xm_context_t*, jar_xm_channel_context_t*, unsigned int flags); | ||
| 1074 | static void jar_xm_cut_note(jar_xm_channel_context_t*); | ||
| 1075 | static void jar_xm_key_off(jar_xm_channel_context_t*); | ||
| 1076 | |||
| 1077 | static void jar_xm_post_pattern_change(jar_xm_context_t*); | ||
| 1078 | static void jar_xm_row(jar_xm_context_t*); | ||
| 1079 | static void jar_xm_tick(jar_xm_context_t*); | ||
| 1080 | |||
| 1081 | static void jar_xm_next_of_sample(jar_xm_context_t*, jar_xm_channel_context_t*, int); | ||
| 1082 | static void jar_xm_mixdown(jar_xm_context_t*, float*, float*); | ||
| 1083 | |||
| 1084 | #define jar_xm_TRIGGER_KEEP_VOLUME (1 << 0) | ||
| 1085 | #define jar_xm_TRIGGER_KEEP_PERIOD (1 << 1) | ||
| 1086 | #define jar_xm_TRIGGER_KEEP_SAMPLE_POSITION (1 << 2) | ||
| 1087 | |||
| 1088 | // C-2, C#2, D-2, D#2, E-2, F-2, F#2, G-2, G#2, A-2, A#2, B-2, C-3 | ||
| 1089 | static const uint16_t amiga_frequencies[] = { 1712, 1616, 1525, 1440, 1357, 1281, 1209, 1141, 1077, 1017, 961, 907, 856 }; | ||
| 1090 | |||
| 1091 | // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f | ||
| 1092 | static const float multi_retrig_add[] = { 0.f, -1.f, -2.f, -4.f, -8.f, -16.f, 0.f, 0.f, 0.f, 1.f, 2.f, 4.f, 8.f, 16.f, 0.f, 0.f }; | ||
| 1093 | |||
| 1094 | // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f | ||
| 1095 | static const float multi_retrig_multiply[] = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, .6666667f, .5f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.5f, 2.f }; | ||
| 1096 | |||
| 1097 | #define jar_xm_CLAMP_UP1F(vol, limit) do { \ | ||
| 1098 | if((vol) > (limit)) (vol) = (limit); \ | ||
| 1099 | } while(0) | ||
| 1100 | #define jar_xm_CLAMP_UP(vol) jar_xm_CLAMP_UP1F((vol), 1.f) | ||
| 1101 | |||
| 1102 | #define jar_xm_CLAMP_DOWN1F(vol, limit) do { \ | ||
| 1103 | if((vol) < (limit)) (vol) = (limit); \ | ||
| 1104 | } while(0) | ||
| 1105 | #define jar_xm_CLAMP_DOWN(vol) jar_xm_CLAMP_DOWN1F((vol), .0f) | ||
| 1106 | |||
| 1107 | #define jar_xm_CLAMP2F(vol, up, down) do { \ | ||
| 1108 | if((vol) > (up)) (vol) = (up); \ | ||
| 1109 | else if((vol) < (down)) (vol) = (down); \ | ||
| 1110 | } while(0) | ||
| 1111 | #define jar_xm_CLAMP(vol) jar_xm_CLAMP2F((vol), 1.f, .0f) | ||
| 1112 | |||
| 1113 | #define jar_xm_SLIDE_TOWARDS(val, goal, incr) do { \ | ||
| 1114 | if((val) > (goal)) { \ | ||
| 1115 | (val) -= (incr); \ | ||
| 1116 | jar_xm_CLAMP_DOWN1F((val), (goal)); \ | ||
| 1117 | } else if((val) < (goal)) { \ | ||
| 1118 | (val) += (incr); \ | ||
| 1119 | jar_xm_CLAMP_UP1F((val), (goal)); \ | ||
| 1120 | } \ | ||
| 1121 | } while(0) | ||
| 1122 | |||
| 1123 | #define jar_xm_LERP(u, v, t) ((u) + (t) * ((v) - (u))) | ||
| 1124 | #define jar_xm_INVERSE_LERP(u, v, lerp) (((lerp) - (u)) / ((v) - (u))) | ||
| 1125 | |||
| 1126 | #define HAS_TONE_PORTAMENTO(s) ((s)->effect_type == 3 \ | ||
| 1127 | || (s)->effect_type == 5 \ | ||
| 1128 | || ((s)->volume_column >> 4) == 0xF) | ||
| 1129 | #define HAS_ARPEGGIO(s) ((s)->effect_type == 0 \ | ||
| 1130 | && (s)->effect_param != 0) | ||
| 1131 | #define HAS_VIBRATO(s) ((s)->effect_type == 4 \ | ||
| 1132 | || (s)->effect_param == 6 \ | ||
| 1133 | || ((s)->volume_column >> 4) == 0xB) | ||
| 1134 | #define NOTE_IS_VALID(n) ((n) > 0 && (n) < 97) | ||
| 1135 | #define NOTE_OFF 97 | ||
| 1136 | |||
| 1137 | static float jar_xm_waveform(jar_xm_waveform_type_t waveform, uint8_t step) { | ||
| 1138 | static unsigned int next_rand = 24492; | ||
| 1139 | step %= 0x40; | ||
| 1140 | switch(waveform) { | ||
| 1141 | case jar_xm_SINE_WAVEFORM: /* No SIN() table used, direct calculation. */ | ||
| 1142 | return -sinf(2.f * 3.141592f * (float)step / (float)0x40); | ||
| 1143 | case jar_xm_RAMP_DOWN_WAVEFORM: /* Ramp down: 1.0f when step = 0; -1.0f when step = 0x40 */ | ||
| 1144 | return (float)(0x20 - step) / 0x20; | ||
| 1145 | case jar_xm_SQUARE_WAVEFORM: /* Square with a 50% duty */ | ||
| 1146 | return (step >= 0x20) ? 1.f : -1.f; | ||
| 1147 | case jar_xm_RANDOM_WAVEFORM: /* Use the POSIX.1-2001 example, just to be deterministic across different machines */ | ||
| 1148 | next_rand = next_rand * 1103515245 + 12345; | ||
| 1149 | return (float)((next_rand >> 16) & 0x7FFF) / (float)0x4000 - 1.f; | ||
| 1150 | case jar_xm_RAMP_UP_WAVEFORM: /* Ramp up: -1.f when step = 0; 1.f when step = 0x40 */ | ||
| 1151 | return (float)(step - 0x20) / 0x20; | ||
| 1152 | default: | ||
| 1153 | break; | ||
| 1154 | } | ||
| 1155 | return .0f; | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | static void jar_xm_autovibrato(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) { | ||
| 1159 | if(ch->instrument == NULL || ch->instrument->vibrato_depth == 0) return; | ||
| 1160 | jar_xm_instrument_t* instr = ch->instrument; | ||
| 1161 | float sweep = 1.f; | ||
| 1162 | if(ch->autovibrato_ticks < instr->vibrato_sweep) { sweep = jar_xm_LERP(0.f, 1.f, (float)ch->autovibrato_ticks / (float)instr->vibrato_sweep); } | ||
| 1163 | unsigned int step = ((ch->autovibrato_ticks++) * instr->vibrato_rate) >> 2; | ||
| 1164 | ch->autovibrato_note_offset = .25f * jar_xm_waveform(instr->vibrato_type, step) * (float)instr->vibrato_depth / (float)0xF * sweep; | ||
| 1165 | jar_xm_update_frequency(ctx, ch); | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | static void jar_xm_vibrato(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t pos) { | ||
| 1169 | unsigned int step = pos * (param >> 4); | ||
| 1170 | ch->vibrato_note_offset = 2.f * jar_xm_waveform(ch->vibrato_waveform, step) * (float)(param & 0x0F) / (float)0xF; | ||
| 1171 | jar_xm_update_frequency(ctx, ch); | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | static void jar_xm_tremolo(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t pos) { | ||
| 1175 | unsigned int step = pos * (param >> 4); | ||
| 1176 | ch->tremolo_volume = -1.f * jar_xm_waveform(ch->tremolo_waveform, step) * (float)(param & 0x0F) / (float)0xF; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | static void jar_xm_arpeggio(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t tick) { | ||
| 1180 | switch(tick % 3) { | ||
| 1181 | case 0: | ||
| 1182 | ch->arp_in_progress = false; | ||
| 1183 | ch->arp_note_offset = 0; | ||
| 1184 | break; | ||
| 1185 | case 2: | ||
| 1186 | ch->arp_in_progress = true; | ||
| 1187 | ch->arp_note_offset = param >> 4; | ||
| 1188 | break; | ||
| 1189 | case 1: | ||
| 1190 | ch->arp_in_progress = true; | ||
| 1191 | ch->arp_note_offset = param & 0x0F; | ||
| 1192 | break; | ||
| 1193 | } | ||
| 1194 | jar_xm_update_frequency(ctx, ch); | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | static void jar_xm_tone_portamento(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) { | ||
| 1198 | /* 3xx called without a note, wait until we get an actual target note. */ | ||
| 1199 | if(ch->tone_portamento_target_period == 0.f) return; /* no value, exit */ | ||
| 1200 | if(ch->period != ch->tone_portamento_target_period) { | ||
| 1201 | jar_xm_SLIDE_TOWARDS(ch->period, ch->tone_portamento_target_period, (ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES ? 4.f : 1.f) * ch->tone_portamento_param); | ||
| 1202 | jar_xm_update_frequency(ctx, ch); | ||
| 1203 | } | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | static void jar_xm_pitch_slide(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, float period_offset) { | ||
| 1207 | /* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear. */ | ||
| 1208 | if(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES) {period_offset *= 4.f; } | ||
| 1209 | ch->period += period_offset; | ||
| 1210 | jar_xm_CLAMP_DOWN(ch->period); | ||
| 1211 | /* XXX: upper bound of period ? */ | ||
| 1212 | jar_xm_update_frequency(ctx, ch); | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | static void jar_xm_panning_slide(jar_xm_channel_context_t* ch, uint8_t rawval) { | ||
| 1216 | if (rawval & 0xF0) {ch->panning += (float)((rawval & 0xF0 )>> 4) / (float)0xFF;}; | ||
| 1217 | if (rawval & 0x0F) {ch->panning -= (float)(rawval & 0x0F) / (float)0xFF;}; | ||
| 1218 | }; | ||
| 1219 | |||
| 1220 | static void jar_xm_volume_slide(jar_xm_channel_context_t* ch, uint8_t rawval) { | ||
| 1221 | if (rawval & 0xF0) {ch->volume += (float)((rawval & 0xF0) >> 4) / (float)0x40;}; | ||
| 1222 | if (rawval & 0x0F) {ch->volume -= (float)(rawval & 0x0F) / (float)0x40;}; | ||
| 1223 | }; | ||
| 1224 | |||
| 1225 | static float jar_xm_envelope_lerp(jar_xm_envelope_point_t* a, jar_xm_envelope_point_t* b, uint16_t pos) { | ||
| 1226 | /* Linear interpolation between two envelope points */ | ||
| 1227 | if(pos <= a->frame) return a->value; | ||
| 1228 | else if(pos >= b->frame) return b->value; | ||
| 1229 | else { | ||
| 1230 | float p = (float)(pos - a->frame) / (float)(b->frame - a->frame); | ||
| 1231 | return a->value * (1 - p) + b->value * p; | ||
| 1232 | } | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | static void jar_xm_post_pattern_change(jar_xm_context_t* ctx) { | ||
| 1236 | /* Loop if necessary */ | ||
| 1237 | if(ctx->current_table_index >= ctx->module.length) { | ||
| 1238 | ctx->current_table_index = ctx->module.restart_position; | ||
| 1239 | ctx->tempo =ctx->default_tempo; // reset to file default value | ||
| 1240 | ctx->bpm = ctx->default_bpm; // reset to file default value | ||
| 1241 | ctx->global_volume = ctx->default_global_volume; // reset to file default value | ||
| 1242 | } | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | static float jar_xm_linear_period(float note) { | ||
| 1246 | return 7680.f - note * 64.f; | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | static float jar_xm_linear_frequency(float period) { | ||
| 1250 | return 8363.f * powf(2.f, (4608.f - period) / 768.f); | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | static float jar_xm_amiga_period(float note) { | ||
| 1254 | unsigned int intnote = note; | ||
| 1255 | uint8_t a = intnote % 12; | ||
| 1256 | int8_t octave = note / 12.f - 2; | ||
| 1257 | uint16_t p1 = amiga_frequencies[a], p2 = amiga_frequencies[a + 1]; | ||
| 1258 | if(octave > 0) { | ||
| 1259 | p1 >>= octave; | ||
| 1260 | p2 >>= octave; | ||
| 1261 | } else if(octave < 0) { | ||
| 1262 | p1 <<= -octave; | ||
| 1263 | p2 <<= -octave; | ||
| 1264 | } | ||
| 1265 | return jar_xm_LERP(p1, p2, note - intnote); | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | static float jar_xm_amiga_frequency(float period) { | ||
| 1269 | if(period == .0f) return .0f; | ||
| 1270 | return 7093789.2f / (period * 2.f); /* This is the PAL value. (we could use the NTSC value also) */ | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | static float jar_xm_period(jar_xm_context_t* ctx, float note) { | ||
| 1274 | switch(ctx->module.frequency_type) { | ||
| 1275 | case jar_xm_LINEAR_FREQUENCIES: | ||
| 1276 | return jar_xm_linear_period(note); | ||
| 1277 | case jar_xm_AMIGA_FREQUENCIES: | ||
| 1278 | return jar_xm_amiga_period(note); | ||
| 1279 | } | ||
| 1280 | return .0f; | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | static float jar_xm_frequency(jar_xm_context_t* ctx, float period, float note_offset) { | ||
| 1284 | switch(ctx->module.frequency_type) { | ||
| 1285 | case jar_xm_LINEAR_FREQUENCIES: | ||
| 1286 | return jar_xm_linear_frequency(period - 64.f * note_offset); | ||
| 1287 | case jar_xm_AMIGA_FREQUENCIES: | ||
| 1288 | if(note_offset == 0) { return jar_xm_amiga_frequency(period); }; | ||
| 1289 | int8_t octave; | ||
| 1290 | float note; | ||
| 1291 | uint16_t p1, p2; | ||
| 1292 | uint8_t a = octave = 0; | ||
| 1293 | |||
| 1294 | /* Find the octave of the current period */ | ||
| 1295 | if(period > amiga_frequencies[0]) { | ||
| 1296 | --octave; | ||
| 1297 | while(period > (amiga_frequencies[0] << -octave)) --octave; | ||
| 1298 | } else if(period < amiga_frequencies[12]) { | ||
| 1299 | ++octave; | ||
| 1300 | while(period < (amiga_frequencies[12] >> octave)) ++octave; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | /* Find the smallest note closest to the current period */ | ||
| 1304 | for(uint8_t i = 0; i < 12; ++i) { | ||
| 1305 | p1 = amiga_frequencies[i], p2 = amiga_frequencies[i + 1]; | ||
| 1306 | if(octave > 0) { | ||
| 1307 | p1 >>= octave; | ||
| 1308 | p2 >>= octave; | ||
| 1309 | } else if(octave < 0) { | ||
| 1310 | p1 <<= (-octave); | ||
| 1311 | p2 <<= (-octave); | ||
| 1312 | } | ||
| 1313 | if(p2 <= period && period <= p1) { | ||
| 1314 | a = i; | ||
| 1315 | break; | ||
| 1316 | } | ||
| 1317 | } | ||
| 1318 | if(JAR_XM_DEBUG && (p1 < period || p2 > period)) { DEBUG("%i <= %f <= %i should hold but doesn't, this is a bug", p2, period, p1); } | ||
| 1319 | note = 12.f * (octave + 2) + a + jar_xm_INVERSE_LERP(p1, p2, period); | ||
| 1320 | return jar_xm_amiga_frequency(jar_xm_amiga_period(note + note_offset)); | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | return .0f; | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | static void jar_xm_update_frequency(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) { | ||
| 1327 | ch->frequency = jar_xm_frequency( ctx, ch->period, (ch->arp_note_offset > 0 ? ch->arp_note_offset : ( ch->vibrato_note_offset + ch->autovibrato_note_offset )) ); | ||
| 1328 | ch->step = ch->frequency / ctx->rate; | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | static void jar_xm_handle_note_and_instrument(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, jar_xm_pattern_slot_t* s) { | ||
| 1332 | jar_xm_module_t* mod = &(ctx->module); | ||
| 1333 | if(s->instrument > 0) { | ||
| 1334 | if(HAS_TONE_PORTAMENTO(ch->current) && ch->instrument != NULL && ch->sample != NULL) { /* Tone portamento in effect */ | ||
| 1335 | jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_PERIOD | jar_xm_TRIGGER_KEEP_SAMPLE_POSITION); | ||
| 1336 | } else if(s->instrument > ctx->module.num_instruments) { /* Invalid instrument, Cut current note */ | ||
| 1337 | jar_xm_cut_note(ch); | ||
| 1338 | ch->instrument = NULL; | ||
| 1339 | ch->sample = NULL; | ||
| 1340 | } else { | ||
| 1341 | ch->instrument = ctx->module.instruments + (s->instrument - 1); | ||
| 1342 | if(s->note == 0 && ch->sample != NULL) { /* Ghost instrument, trigger note */ | ||
| 1343 | /* Sample position is kept, but envelopes are reset */ | ||
| 1344 | jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_SAMPLE_POSITION); | ||
| 1345 | } | ||
| 1346 | } | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | if(NOTE_IS_VALID(s->note)) { | ||
| 1350 | // note value is s->note -1 | ||
| 1351 | jar_xm_instrument_t* instr = ch->instrument; | ||
| 1352 | if(HAS_TONE_PORTAMENTO(ch->current) && instr != NULL && ch->sample != NULL) { | ||
| 1353 | /* Tone portamento in effect */ | ||
| 1354 | ch->note = s->note + ch->sample->relative_note + ch->sample->finetune / 128.f - 1.f; | ||
| 1355 | ch->tone_portamento_target_period = jar_xm_period(ctx, ch->note); | ||
| 1356 | } else if(instr == NULL || ch->instrument->num_samples == 0) { /* Issue on instrument */ | ||
| 1357 | jar_xm_cut_note(ch); | ||
| 1358 | } else { | ||
| 1359 | if(instr->sample_of_notes[s->note - 1] < instr->num_samples) { | ||
| 1360 | if (mod->ramping) { | ||
| 1361 | for(int i = 0; i < jar_xm_SAMPLE_RAMPING_POINTS; ++i) { | ||
| 1362 | jar_xm_next_of_sample(ctx, ch, i); | ||
| 1363 | } | ||
| 1364 | ch->frame_count = 0; | ||
| 1365 | }; | ||
| 1366 | ch->sample = instr->samples + instr->sample_of_notes[s->note - 1]; | ||
| 1367 | ch->orig_note = ch->note = s->note + ch->sample->relative_note + ch->sample->finetune / 128.f - 1.f; | ||
| 1368 | if(s->instrument > 0) { | ||
| 1369 | jar_xm_trigger_note(ctx, ch, 0); | ||
| 1370 | } else { /* Ghost note: keep old volume */ | ||
| 1371 | jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_VOLUME); | ||
| 1372 | } | ||
| 1373 | } else { | ||
| 1374 | jar_xm_cut_note(ch); | ||
| 1375 | } | ||
| 1376 | } | ||
| 1377 | } else if(s->note == NOTE_OFF) { | ||
| 1378 | jar_xm_key_off(ch); | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | // Interpret Effect column | ||
| 1382 | switch(s->effect_type) { | ||
| 1383 | case 1: /* 1xx: Portamento up */ | ||
| 1384 | if(s->effect_param > 0) { ch->portamento_up_param = s->effect_param; } | ||
| 1385 | break; | ||
| 1386 | case 2: /* 2xx: Portamento down */ | ||
| 1387 | if(s->effect_param > 0) { ch->portamento_down_param = s->effect_param; } | ||
| 1388 | break; | ||
| 1389 | case 3: /* 3xx: Tone portamento */ | ||
| 1390 | if(s->effect_param > 0) { ch->tone_portamento_param = s->effect_param; } | ||
| 1391 | break; | ||
| 1392 | case 4: /* 4xy: Vibrato */ | ||
| 1393 | if(s->effect_param & 0x0F) { ch->vibrato_param = (ch->vibrato_param & 0xF0) | (s->effect_param & 0x0F); } /* Set vibrato depth */ | ||
| 1394 | if(s->effect_param >> 4) { ch->vibrato_param = (s->effect_param & 0xF0) | (ch->vibrato_param & 0x0F); } /* Set vibrato speed */ | ||
| 1395 | break; | ||
| 1396 | case 5: /* 5xy: Tone portamento + Volume slide */ | ||
| 1397 | if(s->effect_param > 0) { ch->volume_slide_param = s->effect_param; } | ||
| 1398 | break; | ||
| 1399 | case 6: /* 6xy: Vibrato + Volume slide */ | ||
| 1400 | if(s->effect_param > 0) { ch->volume_slide_param = s->effect_param; } | ||
| 1401 | break; | ||
| 1402 | case 7: /* 7xy: Tremolo */ | ||
| 1403 | if(s->effect_param & 0x0F) { ch->tremolo_param = (ch->tremolo_param & 0xF0) | (s->effect_param & 0x0F); } /* Set tremolo depth */ | ||
| 1404 | if(s->effect_param >> 4) { ch->tremolo_param = (s->effect_param & 0xF0) | (ch->tremolo_param & 0x0F); } /* Set tremolo speed */ | ||
| 1405 | break; | ||
| 1406 | case 8: /* 8xx: Set panning */ | ||
| 1407 | ch->panning = (float)s->effect_param / 255.f; | ||
| 1408 | break; | ||
| 1409 | case 9: /* 9xx: Sample offset */ | ||
| 1410 | if(ch->sample != 0) { //&& NOTE_IS_VALID(s->note)) { | ||
| 1411 | uint32_t final_offset = s->effect_param << (ch->sample->bits == 16 ? 7 : 8); | ||
| 1412 | switch (ch->sample->loop_type) { | ||
| 1413 | case jar_xm_NO_LOOP: | ||
| 1414 | if(final_offset >= ch->sample->length) { /* Pretend the sample dosen't loop and is done playing */ | ||
| 1415 | ch->sample_position = -1; | ||
| 1416 | } else { | ||
| 1417 | ch->sample_position = final_offset; | ||
| 1418 | } | ||
| 1419 | break; | ||
| 1420 | case jar_xm_FORWARD_LOOP: | ||
| 1421 | if (final_offset >= ch->sample->loop_end) { | ||
| 1422 | ch->sample_position -= ch->sample->loop_length; | ||
| 1423 | } else if(final_offset >= ch->sample->length) { | ||
| 1424 | ch->sample_position = ch->sample->loop_start; | ||
| 1425 | } else { | ||
| 1426 | ch->sample_position = final_offset; | ||
| 1427 | } | ||
| 1428 | break; | ||
| 1429 | case jar_xm_PING_PONG_LOOP: | ||
| 1430 | if(final_offset >= ch->sample->loop_end) { | ||
| 1431 | ch->ping = false; | ||
| 1432 | ch->sample_position = (ch->sample->loop_end << 1) - ch->sample_position; | ||
| 1433 | } else if(final_offset >= ch->sample->length) { | ||
| 1434 | ch->ping = false; | ||
| 1435 | ch->sample_position -= ch->sample->length - 1; | ||
| 1436 | } else { | ||
| 1437 | ch->sample_position = final_offset; | ||
| 1438 | }; | ||
| 1439 | break; | ||
| 1440 | } | ||
| 1441 | } | ||
| 1442 | break; | ||
| 1443 | case 0xA: /* Axy: Volume slide */ | ||
| 1444 | if(s->effect_param > 0) { ch->volume_slide_param = s->effect_param; } | ||
| 1445 | break; | ||
| 1446 | case 0xB: /* Bxx: Position jump */ | ||
| 1447 | if(s->effect_param < ctx->module.length) { | ||
| 1448 | ctx->position_jump = true; | ||
| 1449 | ctx->jump_dest = s->effect_param; | ||
| 1450 | } | ||
| 1451 | break; | ||
| 1452 | case 0xC: /* Cxx: Set volume */ | ||
| 1453 | ch->volume = (float)((s->effect_param > 0x40) ? 0x40 : s->effect_param) / (float)0x40; | ||
| 1454 | break; | ||
| 1455 | case 0xD: /* Dxx: Pattern break */ | ||
| 1456 | /* Jump after playing this line */ | ||
| 1457 | ctx->pattern_break = true; | ||
| 1458 | ctx->jump_row = (s->effect_param >> 4) * 10 + (s->effect_param & 0x0F); | ||
| 1459 | break; | ||
| 1460 | case 0xE: /* EXy: Extended command */ | ||
| 1461 | switch(s->effect_param >> 4) { | ||
| 1462 | case 1: /* E1y: Fine portamento up */ | ||
| 1463 | if(s->effect_param & 0x0F) { ch->fine_portamento_up_param = s->effect_param & 0x0F; } | ||
| 1464 | jar_xm_pitch_slide(ctx, ch, -ch->fine_portamento_up_param); | ||
| 1465 | break; | ||
| 1466 | case 2: /* E2y: Fine portamento down */ | ||
| 1467 | if(s->effect_param & 0x0F) { ch->fine_portamento_down_param = s->effect_param & 0x0F; } | ||
| 1468 | jar_xm_pitch_slide(ctx, ch, ch->fine_portamento_down_param); | ||
| 1469 | break; | ||
| 1470 | case 4: /* E4y: Set vibrato control */ | ||
| 1471 | ch->vibrato_waveform = s->effect_param & 3; | ||
| 1472 | ch->vibrato_waveform_retrigger = !((s->effect_param >> 2) & 1); | ||
| 1473 | break; | ||
| 1474 | case 5: /* E5y: Set finetune */ | ||
| 1475 | if(NOTE_IS_VALID(ch->current->note) && ch->sample != NULL) { | ||
| 1476 | ch->note = ch->current->note + ch->sample->relative_note + (float)(((s->effect_param & 0x0F) - 8) << 4) / 128.f - 1.f; | ||
| 1477 | ch->period = jar_xm_period(ctx, ch->note); | ||
| 1478 | jar_xm_update_frequency(ctx, ch); | ||
| 1479 | } | ||
| 1480 | break; | ||
| 1481 | case 6: /* E6y: Pattern loop */ | ||
| 1482 | if(s->effect_param & 0x0F) { | ||
| 1483 | if((s->effect_param & 0x0F) == ch->pattern_loop_count) { /* Loop is over */ | ||
| 1484 | ch->pattern_loop_count = 0; | ||
| 1485 | ctx->position_jump = false; | ||
| 1486 | } else { /* Jump to the beginning of the loop */ | ||
| 1487 | ch->pattern_loop_count++; | ||
| 1488 | ctx->position_jump = true; | ||
| 1489 | ctx->jump_row = ch->pattern_loop_origin; | ||
| 1490 | ctx->jump_dest = ctx->current_table_index; | ||
| 1491 | } | ||
| 1492 | } else { | ||
| 1493 | ch->pattern_loop_origin = ctx->current_row; /* Set loop start point */ | ||
| 1494 | ctx->jump_row = ch->pattern_loop_origin; /* Replicate FT2 E60 bug */ | ||
| 1495 | } | ||
| 1496 | break; | ||
| 1497 | case 7: /* E7y: Set tremolo control */ | ||
| 1498 | ch->tremolo_waveform = s->effect_param & 3; | ||
| 1499 | ch->tremolo_waveform_retrigger = !((s->effect_param >> 2) & 1); | ||
| 1500 | break; | ||
| 1501 | case 0xA: /* EAy: Fine volume slide up */ | ||
| 1502 | if(s->effect_param & 0x0F) { ch->fine_volume_slide_param = s->effect_param & 0x0F; } | ||
| 1503 | jar_xm_volume_slide(ch, ch->fine_volume_slide_param << 4); | ||
| 1504 | break; | ||
| 1505 | case 0xB: /* EBy: Fine volume slide down */ | ||
| 1506 | if(s->effect_param & 0x0F) { ch->fine_volume_slide_param = s->effect_param & 0x0F; } | ||
| 1507 | jar_xm_volume_slide(ch, ch->fine_volume_slide_param); | ||
| 1508 | break; | ||
| 1509 | case 0xD: /* EDy: Note delay */ | ||
| 1510 | /* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x != 0) does not. */ | ||
| 1511 | if(s->note == 0 && s->instrument == 0) { | ||
| 1512 | unsigned int flags = jar_xm_TRIGGER_KEEP_VOLUME; | ||
| 1513 | if(ch->current->effect_param & 0x0F) { | ||
| 1514 | ch->note = ch->orig_note; | ||
| 1515 | jar_xm_trigger_note(ctx, ch, flags); | ||
| 1516 | } else { | ||
| 1517 | jar_xm_trigger_note(ctx, ch, flags | jar_xm_TRIGGER_KEEP_PERIOD | jar_xm_TRIGGER_KEEP_SAMPLE_POSITION ); | ||
| 1518 | } | ||
| 1519 | } | ||
| 1520 | break; | ||
| 1521 | |||
| 1522 | case 0xE: /* EEy: Pattern delay */ | ||
| 1523 | ctx->extra_ticks = (ch->current->effect_param & 0x0F) * ctx->tempo; | ||
| 1524 | break; | ||
| 1525 | default: | ||
| 1526 | break; | ||
| 1527 | } | ||
| 1528 | break; | ||
| 1529 | |||
| 1530 | case 0xF: /* Fxx: Set tempo/BPM */ | ||
| 1531 | if(s->effect_param > 0) { | ||
| 1532 | if(s->effect_param <= 0x1F) { // First 32 possible values adjust the ticks (goes into tempo) | ||
| 1533 | ctx->tempo = s->effect_param; | ||
| 1534 | } else { //32 and greater values adjust the BPM | ||
| 1535 | ctx->bpm = s->effect_param; | ||
| 1536 | } | ||
| 1537 | } | ||
| 1538 | break; | ||
| 1539 | |||
| 1540 | case 16: /* Gxx: Set global volume */ | ||
| 1541 | ctx->global_volume = (float)((s->effect_param > 0x40) ? 0x40 : s->effect_param) / (float)0x40; | ||
| 1542 | break; | ||
| 1543 | case 17: /* Hxy: Global volume slide */ | ||
| 1544 | if(s->effect_param > 0) { ch->global_volume_slide_param = s->effect_param; } | ||
| 1545 | break; | ||
| 1546 | case 21: /* Lxx: Set envelope position */ | ||
| 1547 | ch->volume_envelope_frame_count = s->effect_param; | ||
| 1548 | ch->panning_envelope_frame_count = s->effect_param; | ||
| 1549 | break; | ||
| 1550 | case 25: /* Pxy: Panning slide */ | ||
| 1551 | if(s->effect_param > 0) { ch->panning_slide_param = s->effect_param; } | ||
| 1552 | break; | ||
| 1553 | case 27: /* Rxy: Multi retrig note */ | ||
| 1554 | if(s->effect_param > 0) { | ||
| 1555 | if((s->effect_param >> 4) == 0) { /* Keep previous x value */ | ||
| 1556 | ch->multi_retrig_param = (ch->multi_retrig_param & 0xF0) | (s->effect_param & 0x0F); | ||
| 1557 | } else { | ||
| 1558 | ch->multi_retrig_param = s->effect_param; | ||
| 1559 | } | ||
| 1560 | } | ||
| 1561 | break; | ||
| 1562 | case 29: /* Txy: Tremor */ | ||
| 1563 | if(s->effect_param > 0) { ch->tremor_param = s->effect_param; } /* Tremor x and y params are not separately kept in memory, unlike Rxy */ | ||
| 1564 | break; | ||
| 1565 | case 33: /* Xxy: Extra stuff */ | ||
| 1566 | switch(s->effect_param >> 4) { | ||
| 1567 | case 1: /* X1y: Extra fine portamento up */ | ||
| 1568 | if(s->effect_param & 0x0F) { ch->extra_fine_portamento_up_param = s->effect_param & 0x0F; } | ||
| 1569 | jar_xm_pitch_slide(ctx, ch, -1.0f * ch->extra_fine_portamento_up_param); | ||
| 1570 | break; | ||
| 1571 | case 2: /* X2y: Extra fine portamento down */ | ||
| 1572 | if(s->effect_param & 0x0F) { ch->extra_fine_portamento_down_param = s->effect_param & 0x0F; } | ||
| 1573 | jar_xm_pitch_slide(ctx, ch, ch->extra_fine_portamento_down_param); | ||
| 1574 | break; | ||
| 1575 | default: | ||
| 1576 | break; | ||
| 1577 | } | ||
| 1578 | break; | ||
| 1579 | default: | ||
| 1580 | break; | ||
| 1581 | } | ||
| 1582 | } | ||
| 1583 | |||
| 1584 | static void jar_xm_trigger_note(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, unsigned int flags) { | ||
| 1585 | if (!(flags & jar_xm_TRIGGER_KEEP_SAMPLE_POSITION)) { | ||
| 1586 | ch->sample_position = 0.f; | ||
| 1587 | ch->ping = true; | ||
| 1588 | }; | ||
| 1589 | |||
| 1590 | if (!(flags & jar_xm_TRIGGER_KEEP_VOLUME)) { | ||
| 1591 | if(ch->sample != NULL) { | ||
| 1592 | ch->volume = ch->sample->volume; | ||
| 1593 | }; | ||
| 1594 | }; | ||
| 1595 | ch->panning = ch->sample->panning; | ||
| 1596 | ch->sustained = true; | ||
| 1597 | ch->fadeout_volume = ch->volume_envelope_volume = 1.0f; | ||
| 1598 | ch->panning_envelope_panning = .5f; | ||
| 1599 | ch->volume_envelope_frame_count = ch->panning_envelope_frame_count = 0; | ||
| 1600 | ch->vibrato_note_offset = 0.f; | ||
| 1601 | ch->tremolo_volume = 0.f; | ||
| 1602 | ch->tremor_on = false; | ||
| 1603 | ch->autovibrato_ticks = 0; | ||
| 1604 | |||
| 1605 | if(ch->vibrato_waveform_retrigger) { ch->vibrato_ticks = 0; } /* XXX: should the waveform itself also be reset to sine? */ | ||
| 1606 | if(ch->tremolo_waveform_retrigger) { ch->tremolo_ticks = 0; } | ||
| 1607 | if(!(flags & jar_xm_TRIGGER_KEEP_PERIOD)) { | ||
| 1608 | ch->period = jar_xm_period(ctx, ch->note); | ||
| 1609 | jar_xm_update_frequency(ctx, ch); | ||
| 1610 | } | ||
| 1611 | ch->latest_trigger = ctx->generated_samples; | ||
| 1612 | if(ch->instrument != NULL) { ch->instrument->latest_trigger = ctx->generated_samples; } | ||
| 1613 | if(ch->sample != NULL) { ch->sample->latest_trigger = ctx->generated_samples; } | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | static void jar_xm_cut_note(jar_xm_channel_context_t* ch) { | ||
| 1617 | ch->volume = .0f; /* NB: this is not the same as Key Off */ | ||
| 1618 | // ch->curr_left = .0f; | ||
| 1619 | // ch->curr_right = .0f; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | static void jar_xm_key_off(jar_xm_channel_context_t* ch) { | ||
| 1623 | ch->sustained = false; /* Key Off */ | ||
| 1624 | if(ch->instrument == NULL || !ch->instrument->volume_envelope.enabled) { jar_xm_cut_note(ch); } /* If no volume envelope is used, also cut the note */ | ||
| 1625 | } | ||
| 1626 | |||
| 1627 | static void jar_xm_row(jar_xm_context_t* ctx) { | ||
| 1628 | if(ctx->position_jump) { | ||
| 1629 | ctx->current_table_index = ctx->jump_dest; | ||
| 1630 | ctx->current_row = ctx->jump_row; | ||
| 1631 | ctx->position_jump = false; | ||
| 1632 | ctx->pattern_break = false; | ||
| 1633 | ctx->jump_row = 0; | ||
| 1634 | jar_xm_post_pattern_change(ctx); | ||
| 1635 | } else if(ctx->pattern_break) { | ||
| 1636 | ctx->current_table_index++; | ||
| 1637 | ctx->current_row = ctx->jump_row; | ||
| 1638 | ctx->pattern_break = false; | ||
| 1639 | ctx->jump_row = 0; | ||
| 1640 | jar_xm_post_pattern_change(ctx); | ||
| 1641 | } | ||
| 1642 | jar_xm_pattern_t* cur = ctx->module.patterns + ctx->module.pattern_table[ctx->current_table_index]; | ||
| 1643 | bool in_a_loop = false; | ||
| 1644 | |||
| 1645 | /* Read notes information for all channels into temporary pattern slot */ | ||
| 1646 | for(uint8_t i = 0; i < ctx->module.num_channels; ++i) { | ||
| 1647 | jar_xm_pattern_slot_t* s = cur->slots + ctx->current_row * ctx->module.num_channels + i; | ||
| 1648 | jar_xm_channel_context_t* ch = ctx->channels + i; | ||
| 1649 | ch->current = s; | ||
| 1650 | // If there is no note delay effect (0xED) then... | ||
| 1651 | if(s->effect_type != 0xE || s->effect_param >> 4 != 0xD) { | ||
| 1652 | //********** Process the channel slot information ********** | ||
| 1653 | jar_xm_handle_note_and_instrument(ctx, ch, s); | ||
| 1654 | } else { | ||
| 1655 | // read the note delay information | ||
| 1656 | ch->note_delay_param = s->effect_param & 0x0F; | ||
| 1657 | } | ||
| 1658 | if(!in_a_loop && ch->pattern_loop_count > 0) { | ||
| 1659 | // clarify if in a loop or not | ||
| 1660 | in_a_loop = true; | ||
| 1661 | } | ||
| 1662 | } | ||
| 1663 | |||
| 1664 | if(!in_a_loop) { | ||
| 1665 | /* No E6y loop is in effect (or we are in the first pass) */ | ||
| 1666 | ctx->loop_count = (ctx->row_loop_count[MAX_NUM_ROWS * ctx->current_table_index + ctx->current_row]++); | ||
| 1667 | } | ||
| 1668 | |||
| 1669 | /// Move to next row | ||
| 1670 | ctx->current_row++; /* uint8 warning: can increment from 255 to 0, in which case it is still necessary to go the next pattern. */ | ||
| 1671 | if (!ctx->position_jump && !ctx->pattern_break && (ctx->current_row >= cur->num_rows || ctx->current_row == 0)) { | ||
| 1672 | ctx->current_table_index++; | ||
| 1673 | ctx->current_row = ctx->jump_row; /* This will be 0 most of the time, except when E60 is used */ | ||
| 1674 | ctx->jump_row = 0; | ||
| 1675 | jar_xm_post_pattern_change(ctx); | ||
| 1676 | } | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | static void jar_xm_envelope_tick(jar_xm_channel_context_t *ch, jar_xm_envelope_t *env, uint16_t *counter, float *outval) { | ||
| 1680 | if(env->num_points < 2) { | ||
| 1681 | if(env->num_points == 1) { | ||
| 1682 | *outval = (float)env->points[0].value / (float)0x40; | ||
| 1683 | if(*outval > 1) { *outval = 1; }; | ||
| 1684 | } else {; | ||
| 1685 | return; | ||
| 1686 | }; | ||
| 1687 | } else { | ||
| 1688 | if(env->loop_enabled) { | ||
| 1689 | uint16_t loop_start = env->points[env->loop_start_point].frame; | ||
| 1690 | uint16_t loop_end = env->points[env->loop_end_point].frame; | ||
| 1691 | uint16_t loop_length = loop_end - loop_start; | ||
| 1692 | if(*counter >= loop_end) { *counter -= loop_length; }; | ||
| 1693 | }; | ||
| 1694 | for(uint8_t j = 0; j < (env->num_points - 1); ++j) { | ||
| 1695 | if(env->points[j].frame <= *counter && env->points[j+1].frame >= *counter) { | ||
| 1696 | *outval = jar_xm_envelope_lerp(env->points + j, env->points + j + 1, *counter) / (float)0x40; | ||
| 1697 | break; | ||
| 1698 | }; | ||
| 1699 | }; | ||
| 1700 | /* Make sure it is safe to increment frame count */ | ||
| 1701 | if(!ch->sustained || !env->sustain_enabled || *counter != env->points[env->sustain_point].frame) { (*counter)++; }; | ||
| 1702 | }; | ||
| 1703 | }; | ||
| 1704 | |||
| 1705 | static void jar_xm_envelopes(jar_xm_channel_context_t *ch) { | ||
| 1706 | if(ch->instrument != NULL) { | ||
| 1707 | if(ch->instrument->volume_envelope.enabled) { | ||
| 1708 | if(!ch->sustained) { | ||
| 1709 | ch->fadeout_volume -= (float)ch->instrument->volume_fadeout / 65536.f; | ||
| 1710 | jar_xm_CLAMP_DOWN(ch->fadeout_volume); | ||
| 1711 | }; | ||
| 1712 | jar_xm_envelope_tick(ch, &(ch->instrument->volume_envelope), &(ch->volume_envelope_frame_count), &(ch->volume_envelope_volume)); | ||
| 1713 | }; | ||
| 1714 | if(ch->instrument->panning_envelope.enabled) { | ||
| 1715 | jar_xm_envelope_tick(ch, &(ch->instrument->panning_envelope), &(ch->panning_envelope_frame_count), &(ch->panning_envelope_panning)); | ||
| 1716 | }; | ||
| 1717 | }; | ||
| 1718 | }; | ||
| 1719 | |||
| 1720 | static void jar_xm_tick(jar_xm_context_t* ctx) { | ||
| 1721 | if(ctx->current_tick == 0) { | ||
| 1722 | jar_xm_row(ctx); // We have processed all ticks and we run the row | ||
| 1723 | } | ||
| 1724 | |||
| 1725 | jar_xm_module_t* mod = &(ctx->module); | ||
| 1726 | for(uint8_t i = 0; i < ctx->module.num_channels; ++i) { | ||
| 1727 | jar_xm_channel_context_t* ch = ctx->channels + i; | ||
| 1728 | jar_xm_envelopes(ch); | ||
| 1729 | jar_xm_autovibrato(ctx, ch); | ||
| 1730 | if(ch->arp_in_progress && !HAS_ARPEGGIO(ch->current)) { | ||
| 1731 | ch->arp_in_progress = false; | ||
| 1732 | ch->arp_note_offset = 0; | ||
| 1733 | jar_xm_update_frequency(ctx, ch); | ||
| 1734 | } | ||
| 1735 | if(ch->vibrato_in_progress && !HAS_VIBRATO(ch->current)) { | ||
| 1736 | ch->vibrato_in_progress = false; | ||
| 1737 | ch->vibrato_note_offset = 0.f; | ||
| 1738 | jar_xm_update_frequency(ctx, ch); | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | // Effects in volumne column mostly handled on a per tick basis | ||
| 1742 | switch(ch->current->volume_column & 0xF0) { | ||
| 1743 | case 0x50: // Checks for volume = 64 | ||
| 1744 | if(ch->current->volume_column != 0x50) break; | ||
| 1745 | case 0x10: // Set volume 0-15 | ||
| 1746 | case 0x20: // Set volume 16-32 | ||
| 1747 | case 0x30: // Set volume 32-48 | ||
| 1748 | case 0x40: // Set volume 48-64 | ||
| 1749 | ch->volume = (float)(ch->current->volume_column - 16) / 64.0f; | ||
| 1750 | break; | ||
| 1751 | case 0x60: // Volume slide down | ||
| 1752 | jar_xm_volume_slide(ch, ch->current->volume_column & 0x0F); | ||
| 1753 | break; | ||
| 1754 | case 0x70: // Volume slide up | ||
| 1755 | jar_xm_volume_slide(ch, ch->current->volume_column << 4); | ||
| 1756 | break; | ||
| 1757 | case 0x80: // Fine volume slide down | ||
| 1758 | jar_xm_volume_slide(ch, ch->current->volume_column & 0x0F); | ||
| 1759 | break; | ||
| 1760 | case 0x90: // Fine volume slide up | ||
| 1761 | jar_xm_volume_slide(ch, ch->current->volume_column << 4); | ||
| 1762 | break; | ||
| 1763 | case 0xA0: // Set vibrato speed | ||
| 1764 | ch->vibrato_param = (ch->vibrato_param & 0x0F) | ((ch->current->volume_column & 0x0F) << 4); | ||
| 1765 | break; | ||
| 1766 | case 0xB0: // Vibrato | ||
| 1767 | ch->vibrato_in_progress = false; | ||
| 1768 | jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++); | ||
| 1769 | break; | ||
| 1770 | case 0xC0: // Set panning | ||
| 1771 | if(!ctx->current_tick ) { | ||
| 1772 | ch->panning = (float)(ch->current->volume_column & 0x0F) / 15.0f; | ||
| 1773 | } | ||
| 1774 | break; | ||
| 1775 | case 0xD0: // Panning slide left | ||
| 1776 | jar_xm_panning_slide(ch, ch->current->volume_column & 0x0F); | ||
| 1777 | break; | ||
| 1778 | case 0xE0: // Panning slide right | ||
| 1779 | jar_xm_panning_slide(ch, ch->current->volume_column << 4); | ||
| 1780 | break; | ||
| 1781 | case 0xF0: // Tone portamento | ||
| 1782 | if(!ctx->current_tick ) { | ||
| 1783 | if(ch->current->volume_column & 0x0F) { ch->tone_portamento_param = ((ch->current->volume_column & 0x0F) << 4) | (ch->current->volume_column & 0x0F); } | ||
| 1784 | }; | ||
| 1785 | jar_xm_tone_portamento(ctx, ch); | ||
| 1786 | break; | ||
| 1787 | default: | ||
| 1788 | break; | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | // Only some standard effects handled on a per tick basis | ||
| 1792 | // see jar_xm_handle_note_and_instrument for all effects handling on a per row basis | ||
| 1793 | switch(ch->current->effect_type) { | ||
| 1794 | case 0: /* 0xy: Arpeggio */ | ||
| 1795 | if(ch->current->effect_param > 0) { | ||
| 1796 | char arp_offset = ctx->tempo % 3; | ||
| 1797 | switch(arp_offset) { | ||
| 1798 | case 2: /* 0 -> x -> 0 -> y -> x -> ... */ | ||
| 1799 | if(ctx->current_tick == 1) { | ||
| 1800 | ch->arp_in_progress = true; | ||
| 1801 | ch->arp_note_offset = ch->current->effect_param >> 4; | ||
| 1802 | jar_xm_update_frequency(ctx, ch); | ||
| 1803 | break; | ||
| 1804 | } | ||
| 1805 | /* No break here, this is intended */ | ||
| 1806 | case 1: /* 0 -> 0 -> y -> x -> ... */ | ||
| 1807 | if(ctx->current_tick == 0) { | ||
| 1808 | ch->arp_in_progress = false; | ||
| 1809 | ch->arp_note_offset = 0; | ||
| 1810 | jar_xm_update_frequency(ctx, ch); | ||
| 1811 | break; | ||
| 1812 | } | ||
| 1813 | /* No break here, this is intended */ | ||
| 1814 | case 0: /* 0 -> y -> x -> ... */ | ||
| 1815 | jar_xm_arpeggio(ctx, ch, ch->current->effect_param, ctx->current_tick - arp_offset); | ||
| 1816 | default: | ||
| 1817 | break; | ||
| 1818 | } | ||
| 1819 | } | ||
| 1820 | break; | ||
| 1821 | |||
| 1822 | case 1: /* 1xx: Portamento up */ | ||
| 1823 | if(ctx->current_tick == 0) break; | ||
| 1824 | jar_xm_pitch_slide(ctx, ch, -ch->portamento_up_param); | ||
| 1825 | break; | ||
| 1826 | case 2: /* 2xx: Portamento down */ | ||
| 1827 | if(ctx->current_tick == 0) break; | ||
| 1828 | jar_xm_pitch_slide(ctx, ch, ch->portamento_down_param); | ||
| 1829 | break; | ||
| 1830 | case 3: /* 3xx: Tone portamento */ | ||
| 1831 | if(ctx->current_tick == 0) break; | ||
| 1832 | jar_xm_tone_portamento(ctx, ch); | ||
| 1833 | break; | ||
| 1834 | case 4: /* 4xy: Vibrato */ | ||
| 1835 | if(ctx->current_tick == 0) break; | ||
| 1836 | ch->vibrato_in_progress = true; | ||
| 1837 | jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++); | ||
| 1838 | break; | ||
| 1839 | case 5: /* 5xy: Tone portamento + Volume slide */ | ||
| 1840 | if(ctx->current_tick == 0) break; | ||
| 1841 | jar_xm_tone_portamento(ctx, ch); | ||
| 1842 | jar_xm_volume_slide(ch, ch->volume_slide_param); | ||
| 1843 | break; | ||
| 1844 | case 6: /* 6xy: Vibrato + Volume slide */ | ||
| 1845 | if(ctx->current_tick == 0) break; | ||
| 1846 | ch->vibrato_in_progress = true; | ||
| 1847 | jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++); | ||
| 1848 | jar_xm_volume_slide(ch, ch->volume_slide_param); | ||
| 1849 | break; | ||
| 1850 | case 7: /* 7xy: Tremolo */ | ||
| 1851 | if(ctx->current_tick == 0) break; | ||
| 1852 | jar_xm_tremolo(ctx, ch, ch->tremolo_param, ch->tremolo_ticks++); | ||
| 1853 | break; | ||
| 1854 | case 8: /* 8xy: Set panning */ | ||
| 1855 | break; | ||
| 1856 | case 9: /* 9xy: Sample offset */ | ||
| 1857 | break; | ||
| 1858 | case 0xA: /* Axy: Volume slide */ | ||
| 1859 | if(ctx->current_tick == 0) break; | ||
| 1860 | jar_xm_volume_slide(ch, ch->volume_slide_param); | ||
| 1861 | break; | ||
| 1862 | case 0xE: /* EXy: Extended command */ | ||
| 1863 | switch(ch->current->effect_param >> 4) { | ||
| 1864 | case 0x9: /* E9y: Retrigger note */ | ||
| 1865 | if(ctx->current_tick != 0 && ch->current->effect_param & 0x0F) { | ||
| 1866 | if(!(ctx->current_tick % (ch->current->effect_param & 0x0F))) { | ||
| 1867 | jar_xm_trigger_note(ctx, ch, 0); | ||
| 1868 | jar_xm_envelopes(ch); | ||
| 1869 | } | ||
| 1870 | } | ||
| 1871 | break; | ||
| 1872 | case 0xC: /* ECy: Note cut */ | ||
| 1873 | if((ch->current->effect_param & 0x0F) == ctx->current_tick) { | ||
| 1874 | jar_xm_cut_note(ch); | ||
| 1875 | } | ||
| 1876 | break; | ||
| 1877 | case 0xD: /* EDy: Note delay */ | ||
| 1878 | if(ch->note_delay_param == ctx->current_tick) { | ||
| 1879 | jar_xm_handle_note_and_instrument(ctx, ch, ch->current); | ||
| 1880 | jar_xm_envelopes(ch); | ||
| 1881 | } | ||
| 1882 | break; | ||
| 1883 | default: | ||
| 1884 | break; | ||
| 1885 | } | ||
| 1886 | break; | ||
| 1887 | case 16: /* Fxy: Set tempo/BPM */ | ||
| 1888 | break; | ||
| 1889 | case 17: /* Hxy: Global volume slide */ | ||
| 1890 | if(ctx->current_tick == 0) break; | ||
| 1891 | if((ch->global_volume_slide_param & 0xF0) && (ch->global_volume_slide_param & 0x0F)) { break; }; /* Invalid state */ | ||
| 1892 | if(ch->global_volume_slide_param & 0xF0) { /* Global slide up */ | ||
| 1893 | float f = (float)(ch->global_volume_slide_param >> 4) / (float)0x40; | ||
| 1894 | ctx->global_volume += f; | ||
| 1895 | jar_xm_CLAMP_UP(ctx->global_volume); | ||
| 1896 | } else { /* Global slide down */ | ||
| 1897 | float f = (float)(ch->global_volume_slide_param & 0x0F) / (float)0x40; | ||
| 1898 | ctx->global_volume -= f; | ||
| 1899 | jar_xm_CLAMP_DOWN(ctx->global_volume); | ||
| 1900 | }; | ||
| 1901 | break; | ||
| 1902 | |||
| 1903 | case 20: /* Kxx: Key off */ | ||
| 1904 | if(ctx->current_tick == ch->current->effect_param) { jar_xm_key_off(ch); }; | ||
| 1905 | break; | ||
| 1906 | case 21: /* Lxx: Set envelope position */ | ||
| 1907 | break; | ||
| 1908 | case 25: /* Pxy: Panning slide */ | ||
| 1909 | if(ctx->current_tick == 0) break; | ||
| 1910 | jar_xm_panning_slide(ch, ch->panning_slide_param); | ||
| 1911 | break; | ||
| 1912 | case 27: /* Rxy: Multi retrig note */ | ||
| 1913 | if(ctx->current_tick == 0) break; | ||
| 1914 | if(((ch->multi_retrig_param) & 0x0F) == 0) break; | ||
| 1915 | if((ctx->current_tick % (ch->multi_retrig_param & 0x0F)) == 0) { | ||
| 1916 | float v = ch->volume * multi_retrig_multiply[ch->multi_retrig_param >> 4] | ||
| 1917 | + multi_retrig_add[ch->multi_retrig_param >> 4]; | ||
| 1918 | jar_xm_CLAMP(v); | ||
| 1919 | jar_xm_trigger_note(ctx, ch, 0); | ||
| 1920 | ch->volume = v; | ||
| 1921 | }; | ||
| 1922 | break; | ||
| 1923 | |||
| 1924 | case 29: /* Txy: Tremor */ | ||
| 1925 | if(ctx->current_tick == 0) break; | ||
| 1926 | ch->tremor_on = ( (ctx->current_tick - 1) % ((ch->tremor_param >> 4) + (ch->tremor_param & 0x0F) + 2) > (ch->tremor_param >> 4) ); | ||
| 1927 | break; | ||
| 1928 | default: | ||
| 1929 | break; | ||
| 1930 | }; | ||
| 1931 | |||
| 1932 | float panning, volume; | ||
| 1933 | panning = ch->panning + (ch->panning_envelope_panning - .5f) * (.5f - fabs(ch->panning - .5f)) * 2.0f; | ||
| 1934 | if(ch->tremor_on) { | ||
| 1935 | volume = .0f; | ||
| 1936 | } else { | ||
| 1937 | volume = ch->volume + ch->tremolo_volume; | ||
| 1938 | jar_xm_CLAMP(volume); | ||
| 1939 | volume *= ch->fadeout_volume * ch->volume_envelope_volume; | ||
| 1940 | }; | ||
| 1941 | |||
| 1942 | if (mod->ramping) { | ||
| 1943 | ch->target_panning = panning; | ||
| 1944 | ch->target_volume = volume; | ||
| 1945 | } else { | ||
| 1946 | ch->actual_panning = panning; | ||
| 1947 | ch->actual_volume = volume; | ||
| 1948 | }; | ||
| 1949 | }; | ||
| 1950 | |||
| 1951 | ctx->current_tick++; // ok so we understand that ticks increment within the row | ||
| 1952 | if(ctx->current_tick >= ctx->tempo + ctx->extra_ticks) { | ||
| 1953 | // This means it reached the end of the row and we reset | ||
| 1954 | ctx->current_tick = 0; | ||
| 1955 | ctx->extra_ticks = 0; | ||
| 1956 | }; | ||
| 1957 | |||
| 1958 | // Number of ticks / second = BPM * 0.4 | ||
| 1959 | ctx->remaining_samples_in_tick += (float)ctx->rate / ((float)ctx->bpm * 0.4f); | ||
| 1960 | }; | ||
| 1961 | |||
| 1962 | static void jar_xm_next_of_sample(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, int previous) { | ||
| 1963 | jar_xm_module_t* mod = &(ctx->module); | ||
| 1964 | |||
| 1965 | // ch->curr_left = 0.f; | ||
| 1966 | // ch->curr_right = 0.f; | ||
| 1967 | if(ch->instrument == NULL || ch->sample == NULL || ch->sample_position < 0) { | ||
| 1968 | ch->curr_left = 0.f; | ||
| 1969 | ch->curr_right = 0.f; | ||
| 1970 | if (mod->ramping) { | ||
| 1971 | if (ch->frame_count < jar_xm_SAMPLE_RAMPING_POINTS) { | ||
| 1972 | if (previous > -1) { | ||
| 1973 | ch->end_of_previous_sample_left[previous] = jar_xm_LERP(ch->end_of_previous_sample_left[ch->frame_count], ch->curr_left, (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS); | ||
| 1974 | ch->end_of_previous_sample_right[previous] = jar_xm_LERP(ch->end_of_previous_sample_right[ch->frame_count], ch->curr_right, (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS); | ||
| 1975 | } else { | ||
| 1976 | ch->curr_left = jar_xm_LERP(ch->end_of_previous_sample_left[ch->frame_count], ch->curr_left, (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS); | ||
| 1977 | ch->curr_right = jar_xm_LERP(ch->end_of_previous_sample_right[ch->frame_count], ch->curr_right, (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS); | ||
| 1978 | }; | ||
| 1979 | }; | ||
| 1980 | }; | ||
| 1981 | return; | ||
| 1982 | }; | ||
| 1983 | if(ch->sample->length == 0) { | ||
| 1984 | return; | ||
| 1985 | }; | ||
| 1986 | |||
| 1987 | float t = 0.f; | ||
| 1988 | uint32_t b = 0; | ||
| 1989 | if(mod->linear_interpolation) { | ||
| 1990 | b = ch->sample_position + 1; | ||
| 1991 | t = ch->sample_position - (uint32_t)ch->sample_position; /* Cheaper than fmodf(., 1.f) */ | ||
| 1992 | }; | ||
| 1993 | |||
| 1994 | float u_left, u_right; | ||
| 1995 | u_left = ch->sample->data[(uint32_t)ch->sample_position]; | ||
| 1996 | if (ch->sample->stereo) { | ||
| 1997 | u_right = ch->sample->data[(uint32_t)ch->sample_position + ch->sample->length]; | ||
| 1998 | } else { | ||
| 1999 | u_right = u_left; | ||
| 2000 | }; | ||
| 2001 | float v_left = 0.f, v_right = 0.f; | ||
| 2002 | switch(ch->sample->loop_type) { | ||
| 2003 | case jar_xm_NO_LOOP: | ||
| 2004 | if(mod->linear_interpolation) { | ||
| 2005 | v_left = (b < ch->sample->length) ? ch->sample->data[b] : .0f; | ||
| 2006 | if (ch->sample->stereo) { | ||
| 2007 | v_right = (b < ch->sample->length) ? ch->sample->data[b + ch->sample->length] : .0f; | ||
| 2008 | } else { | ||
| 2009 | v_right = v_left; | ||
| 2010 | }; | ||
| 2011 | }; | ||
| 2012 | ch->sample_position += ch->step; | ||
| 2013 | if(ch->sample_position >= ch->sample->length) { ch->sample_position = -1; } // stop playing this sample | ||
| 2014 | break; | ||
| 2015 | case jar_xm_FORWARD_LOOP: | ||
| 2016 | if(mod->linear_interpolation) { | ||
| 2017 | v_left = ch->sample->data[ (b == ch->sample->loop_end) ? ch->sample->loop_start : b ]; | ||
| 2018 | if (ch->sample->stereo) { | ||
| 2019 | v_right = ch->sample->data[ (b == ch->sample->loop_end) ? ch->sample->loop_start + ch->sample->length : b + ch->sample->length]; | ||
| 2020 | } else { | ||
| 2021 | v_right = v_left; | ||
| 2022 | }; | ||
| 2023 | }; | ||
| 2024 | ch->sample_position += ch->step; | ||
| 2025 | if (ch->sample_position >= ch->sample->loop_end) { | ||
| 2026 | ch->sample_position -= ch->sample->loop_length; | ||
| 2027 | }; | ||
| 2028 | if(ch->sample_position >= ch->sample->length) { | ||
| 2029 | ch->sample_position = ch->sample->loop_start; | ||
| 2030 | }; | ||
| 2031 | break; | ||
| 2032 | case jar_xm_PING_PONG_LOOP: | ||
| 2033 | if(ch->ping) { | ||
| 2034 | if(mod->linear_interpolation) { | ||
| 2035 | v_left = (b >= ch->sample->loop_end) ? ch->sample->data[(uint32_t)ch->sample_position] : ch->sample->data[b]; | ||
| 2036 | if (ch->sample->stereo) { | ||
| 2037 | v_right = (b >= ch->sample->loop_end) ? ch->sample->data[(uint32_t)ch->sample_position + ch->sample->length] : ch->sample->data[b + ch->sample->length]; | ||
| 2038 | } else { | ||
| 2039 | v_right = v_left; | ||
| 2040 | }; | ||
| 2041 | }; | ||
| 2042 | ch->sample_position += ch->step; | ||
| 2043 | if(ch->sample_position >= ch->sample->loop_end) { | ||
| 2044 | ch->ping = false; | ||
| 2045 | ch->sample_position = (ch->sample->loop_end << 1) - ch->sample_position; | ||
| 2046 | }; | ||
| 2047 | if(ch->sample_position >= ch->sample->length) { | ||
| 2048 | ch->ping = false; | ||
| 2049 | ch->sample_position -= ch->sample->length - 1; | ||
| 2050 | }; | ||
| 2051 | } else { | ||
| 2052 | if(mod->linear_interpolation) { | ||
| 2053 | v_left = u_left; | ||
| 2054 | v_right = u_right; | ||
| 2055 | u_left = (b == 1 || b - 2 <= ch->sample->loop_start) ? ch->sample->data[(uint32_t)ch->sample_position] : ch->sample->data[b - 2]; | ||
| 2056 | if (ch->sample->stereo) { | ||
| 2057 | u_right = (b == 1 || b - 2 <= ch->sample->loop_start) ? ch->sample->data[(uint32_t)ch->sample_position + ch->sample->length] : ch->sample->data[b + ch->sample->length - 2]; | ||
| 2058 | } else { | ||
| 2059 | u_right = u_left; | ||
| 2060 | }; | ||
| 2061 | }; | ||
| 2062 | ch->sample_position -= ch->step; | ||
| 2063 | if(ch->sample_position <= ch->sample->loop_start) { | ||
| 2064 | ch->ping = true; | ||
| 2065 | ch->sample_position = (ch->sample->loop_start << 1) - ch->sample_position; | ||
| 2066 | }; | ||
| 2067 | if (ch->sample_position <= .0f) { | ||
| 2068 | ch->ping = true; | ||
| 2069 | ch->sample_position = .0f; | ||
| 2070 | }; | ||
| 2071 | }; | ||
| 2072 | break; | ||
| 2073 | |||
| 2074 | default: | ||
| 2075 | v_left = .0f; | ||
| 2076 | v_right = .0f; | ||
| 2077 | break; | ||
| 2078 | }; | ||
| 2079 | |||
| 2080 | float endval_left = mod->linear_interpolation ? jar_xm_LERP(u_left, v_left, t) : u_left; | ||
| 2081 | float endval_right = mod->linear_interpolation ? jar_xm_LERP(u_right, v_right, t) : u_right; | ||
| 2082 | |||
| 2083 | if (mod->ramping) { | ||
| 2084 | if(ch->frame_count < jar_xm_SAMPLE_RAMPING_POINTS) { | ||
| 2085 | /* Smoothly transition between old and new sample. */ | ||
| 2086 | if (previous > -1) { | ||
| 2087 | ch->end_of_previous_sample_left[previous] = jar_xm_LERP(ch->end_of_previous_sample_left[ch->frame_count], endval_left, (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS); | ||
| 2088 | ch->end_of_previous_sample_right[previous] = jar_xm_LERP(ch->end_of_previous_sample_right[ch->frame_count], endval_right, (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS); | ||
| 2089 | } else { | ||
| 2090 | ch->curr_left = jar_xm_LERP(ch->end_of_previous_sample_left[ch->frame_count], endval_left, (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS); | ||
| 2091 | ch->curr_right = jar_xm_LERP(ch->end_of_previous_sample_right[ch->frame_count], endval_right, (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS); | ||
| 2092 | }; | ||
| 2093 | }; | ||
| 2094 | }; | ||
| 2095 | |||
| 2096 | if (previous > -1) { | ||
| 2097 | ch->end_of_previous_sample_left[previous] = endval_left; | ||
| 2098 | ch->end_of_previous_sample_right[previous] = endval_right; | ||
| 2099 | } else { | ||
| 2100 | ch->curr_left = endval_left; | ||
| 2101 | ch->curr_right = endval_right; | ||
| 2102 | }; | ||
| 2103 | }; | ||
| 2104 | |||
| 2105 | // gather all channel audio into stereo float | ||
| 2106 | static void jar_xm_mixdown(jar_xm_context_t* ctx, float* left, float* right) { | ||
| 2107 | jar_xm_module_t* mod = &(ctx->module); | ||
| 2108 | |||
| 2109 | if(ctx->remaining_samples_in_tick <= 0) { | ||
| 2110 | jar_xm_tick(ctx); | ||
| 2111 | }; | ||
| 2112 | ctx->remaining_samples_in_tick--; | ||
| 2113 | *left = 0.f; | ||
| 2114 | *right = 0.f; | ||
| 2115 | if(ctx->max_loop_count > 0 && ctx->loop_count > ctx->max_loop_count) { return; } | ||
| 2116 | |||
| 2117 | for(uint8_t i = 0; i < ctx->module.num_channels; ++i) { | ||
| 2118 | jar_xm_channel_context_t* ch = ctx->channels + i; | ||
| 2119 | if(ch->instrument != NULL && ch->sample != NULL && ch->sample_position >= 0) { | ||
| 2120 | jar_xm_next_of_sample(ctx, ch, -1); | ||
| 2121 | if(!ch->muted && !ch->instrument->muted) { | ||
| 2122 | *left += ch->curr_left * ch->actual_volume * (1.f - ch->actual_panning); | ||
| 2123 | *right += ch->curr_right * ch->actual_volume * ch->actual_panning; | ||
| 2124 | }; | ||
| 2125 | |||
| 2126 | if (mod->ramping) { | ||
| 2127 | ch->frame_count++; | ||
| 2128 | jar_xm_SLIDE_TOWARDS(ch->actual_volume, ch->target_volume, ctx->volume_ramp); | ||
| 2129 | jar_xm_SLIDE_TOWARDS(ch->actual_panning, ch->target_panning, ctx->panning_ramp); | ||
| 2130 | }; | ||
| 2131 | }; | ||
| 2132 | }; | ||
| 2133 | if (ctx->global_volume != 1.0f) { | ||
| 2134 | *left *= ctx->global_volume; | ||
| 2135 | *right *= ctx->global_volume; | ||
| 2136 | }; | ||
| 2137 | |||
| 2138 | // experimental | ||
| 2139 | // float counter = (float)ctx->generated_samples * 0.0001f | ||
| 2140 | // *left = tan(&left + sin(counter)); | ||
| 2141 | // *right = tan(&right + cos(counter)); | ||
| 2142 | |||
| 2143 | // apply brick wall limiter when audio goes beyond bounderies | ||
| 2144 | if(*left < -1.0) {*left = -1.0;} else if(*left > 1.0) {*left = 1.0;}; | ||
| 2145 | if(*right < -1.0) {*right = -1.0;} else if(*right > 1.0) {*right = 1.0;}; | ||
| 2146 | }; | ||
| 2147 | |||
| 2148 | void jar_xm_generate_samples(jar_xm_context_t* ctx, float* output, size_t numsamples) { | ||
| 2149 | if(ctx && output) { | ||
| 2150 | ctx->generated_samples += numsamples; | ||
| 2151 | for(size_t i = 0; i < numsamples; i++) { | ||
| 2152 | jar_xm_mixdown(ctx, output + (2 * i), output + (2 * i + 1)); | ||
| 2153 | }; | ||
| 2154 | }; | ||
| 2155 | }; | ||
| 2156 | |||
| 2157 | uint64_t jar_xm_get_remaining_samples(jar_xm_context_t* ctx) { | ||
| 2158 | uint64_t total = 0; | ||
| 2159 | uint8_t currentLoopCount = jar_xm_get_loop_count(ctx); | ||
| 2160 | jar_xm_set_max_loop_count(ctx, 0); | ||
| 2161 | while(jar_xm_get_loop_count(ctx) == currentLoopCount) { | ||
| 2162 | total += ctx->remaining_samples_in_tick; | ||
| 2163 | ctx->remaining_samples_in_tick = 0; | ||
| 2164 | jar_xm_tick(ctx); | ||
| 2165 | } | ||
| 2166 | ctx->loop_count = currentLoopCount; | ||
| 2167 | return total; | ||
| 2168 | } | ||
| 2169 | |||
| 2170 | //-------------------------------------------- | ||
| 2171 | //FILE LOADER - TODO - NEEDS TO BE CLEANED UP | ||
| 2172 | //-------------------------------------------- | ||
| 2173 | #undef DEBUG | ||
| 2174 | #define DEBUG(...) do { \ | ||
| 2175 | fprintf(stderr, __VA_ARGS__); \ | ||
| 2176 | fflush(stderr); \ | ||
| 2177 | } while(0) | ||
| 2178 | |||
| 2179 | #define DEBUG_ERR(...) do { \ | ||
| 2180 | fprintf(stderr, __VA_ARGS__); \ | ||
| 2181 | fflush(stderr); \ | ||
| 2182 | } while(0) | ||
| 2183 | |||
| 2184 | #define FATAL(...) do { \ | ||
| 2185 | fprintf(stderr, __VA_ARGS__); \ | ||
| 2186 | fflush(stderr); \ | ||
| 2187 | exit(1); \ | ||
| 2188 | } while(0) | ||
| 2189 | |||
| 2190 | #define FATAL_ERR(...) do { \ | ||
| 2191 | fprintf(stderr, __VA_ARGS__); \ | ||
| 2192 | fflush(stderr); \ | ||
| 2193 | exit(1); \ | ||
| 2194 | } while(0) | ||
| 2195 | |||
| 2196 | |||
| 2197 | int jar_xm_create_context_from_file(jar_xm_context_t** ctx, uint32_t rate, const char* filename) { | ||
| 2198 | FILE* xmf; | ||
| 2199 | int size; | ||
| 2200 | int ret; | ||
| 2201 | |||
| 2202 | xmf = fopen(filename, "rb"); | ||
| 2203 | if(xmf == NULL) { | ||
| 2204 | DEBUG_ERR("Could not open input file"); | ||
| 2205 | *ctx = NULL; | ||
| 2206 | return 3; | ||
| 2207 | } | ||
| 2208 | |||
| 2209 | fseek(xmf, 0, SEEK_END); | ||
| 2210 | size = ftell(xmf); | ||
| 2211 | rewind(xmf); | ||
| 2212 | if(size == -1) { | ||
| 2213 | fclose(xmf); | ||
| 2214 | DEBUG_ERR("fseek() failed"); | ||
| 2215 | *ctx = NULL; | ||
| 2216 | return 4; | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | char* data = JARXM_MALLOC(size + 1); | ||
| 2220 | if(!data || fread(data, 1, size, xmf) < size) { | ||
| 2221 | fclose(xmf); | ||
| 2222 | DEBUG_ERR(data ? "fread() failed" : "JARXM_MALLOC() failed"); | ||
| 2223 | JARXM_FREE(data); | ||
| 2224 | *ctx = NULL; | ||
| 2225 | return 5; | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | fclose(xmf); | ||
| 2229 | |||
| 2230 | ret = jar_xm_create_context_safe(ctx, data, size, rate); | ||
| 2231 | JARXM_FREE(data); | ||
| 2232 | |||
| 2233 | switch(ret) { | ||
| 2234 | case 0: | ||
| 2235 | break; | ||
| 2236 | case 1: DEBUG("could not create context: module is not sane\n"); | ||
| 2237 | *ctx = NULL; | ||
| 2238 | return 1; | ||
| 2239 | break; | ||
| 2240 | case 2: FATAL("could not create context: malloc failed\n"); | ||
| 2241 | return 2; | ||
| 2242 | break; | ||
| 2243 | default: FATAL("could not create context: unknown error\n"); | ||
| 2244 | return 6; | ||
| 2245 | break; | ||
| 2246 | } | ||
| 2247 | |||
| 2248 | return 0; | ||
| 2249 | } | ||
| 2250 | |||
| 2251 | // not part of the original library | ||
| 2252 | void jar_xm_reset(jar_xm_context_t* ctx) { | ||
| 2253 | for (uint16_t i = 0; i < jar_xm_get_number_of_channels(ctx); i++) { | ||
| 2254 | jar_xm_cut_note(&ctx->channels[i]); | ||
| 2255 | } | ||
| 2256 | ctx->generated_samples = 0; | ||
| 2257 | ctx->current_row = 0; | ||
| 2258 | ctx->current_table_index = 0; | ||
| 2259 | ctx->current_tick = 0; | ||
| 2260 | ctx->tempo =ctx->default_tempo; // reset to file default value | ||
| 2261 | ctx->bpm = ctx->default_bpm; // reset to file default value | ||
| 2262 | ctx->global_volume = ctx->default_global_volume; // reset to file default value | ||
| 2263 | } | ||
| 2264 | |||
| 2265 | |||
| 2266 | void jar_xm_flip_linear_interpolation(jar_xm_context_t* ctx) { | ||
| 2267 | if (ctx->module.linear_interpolation) { | ||
| 2268 | ctx->module.linear_interpolation = 0; | ||
| 2269 | } else { | ||
| 2270 | ctx->module.linear_interpolation = 1; | ||
| 2271 | } | ||
| 2272 | } | ||
| 2273 | |||
| 2274 | void jar_xm_table_jump(jar_xm_context_t* ctx, int table_ptr) { | ||
| 2275 | for (uint16_t i = 0; i < jar_xm_get_number_of_channels(ctx); i++) { | ||
| 2276 | jar_xm_cut_note(&ctx->channels[i]); | ||
| 2277 | } | ||
| 2278 | ctx->current_row = 0; | ||
| 2279 | ctx->current_tick = 0; | ||
| 2280 | if(table_ptr > 0 && table_ptr < ctx->module.length) { | ||
| 2281 | ctx->current_table_index = table_ptr; | ||
| 2282 | ctx->module.restart_position = table_ptr; // The reason to jump is to start a new loop or track | ||
| 2283 | } else { | ||
| 2284 | ctx->current_table_index = 0; | ||
| 2285 | ctx->module.restart_position = 0; // The reason to jump is to start a new loop or track | ||
| 2286 | ctx->tempo =ctx->default_tempo; // reset to file default value | ||
| 2287 | ctx->bpm = ctx->default_bpm; // reset to file default value | ||
| 2288 | ctx->global_volume = ctx->default_global_volume; // reset to file default value | ||
| 2289 | }; | ||
| 2290 | } | ||
| 2291 | |||
| 2292 | |||
| 2293 | // TRANSLATE NOTE NUMBER INTO USER VALUE (ie. 1 = C-1, 2 = C#1, 3 = D-1 ... ) | ||
| 2294 | const char* xm_note_chr(int number) { | ||
| 2295 | if (number == NOTE_OFF) { | ||
| 2296 | return "=="; | ||
| 2297 | }; | ||
| 2298 | number = number % 12; | ||
| 2299 | switch(number) { | ||
| 2300 | case 1: return "C-"; | ||
| 2301 | case 2: return "C#"; | ||
| 2302 | case 3: return "D-"; | ||
| 2303 | case 4: return "D#"; | ||
| 2304 | case 5: return "E-"; | ||
| 2305 | case 6: return "F-"; | ||
| 2306 | case 7: return "F#"; | ||
| 2307 | case 8: return "G-"; | ||
| 2308 | case 9: return "G#"; | ||
| 2309 | case 10: return "A-"; | ||
| 2310 | case 11: return "A#"; | ||
| 2311 | case 12: return "B-"; | ||
| 2312 | }; | ||
| 2313 | return "??"; | ||
| 2314 | }; | ||
| 2315 | |||
| 2316 | const char* xm_octave_chr(int number) { | ||
| 2317 | if (number == NOTE_OFF) { | ||
| 2318 | return "="; | ||
| 2319 | }; | ||
| 2320 | |||
| 2321 | int number2 = number - number % 12; | ||
| 2322 | int result = floor(number2 / 12) + 1; | ||
| 2323 | switch(result) { | ||
| 2324 | case 1: return "1"; | ||
| 2325 | case 2: return "2"; | ||
| 2326 | case 3: return "3"; | ||
| 2327 | case 4: return "4"; | ||
| 2328 | case 5: return "5"; | ||
| 2329 | case 6: return "6"; | ||
| 2330 | case 7: return "7"; | ||
| 2331 | case 8: return "8"; | ||
| 2332 | default: return "?"; /* UNKNOWN */ | ||
| 2333 | }; | ||
| 2334 | |||
| 2335 | }; | ||
| 2336 | |||
| 2337 | // TRANSLATE NOTE EFFECT CODE INTO USER VALUE | ||
| 2338 | const char* xm_effect_chr(int fx) { | ||
| 2339 | switch(fx) { | ||
| 2340 | case 0: return "0"; /* ZERO = NO EFFECT */ | ||
| 2341 | case 1: return "1"; /* 1xx: Portamento up */ | ||
| 2342 | case 2: return "2"; /* 2xx: Portamento down */ | ||
| 2343 | case 3: return "3"; /* 3xx: Tone portamento */ | ||
| 2344 | case 4: return "4"; /* 4xy: Vibrato */ | ||
| 2345 | case 5: return "5"; /* 5xy: Tone portamento + Volume slide */ | ||
| 2346 | case 6: return "6"; /* 6xy: Vibrato + Volume slide */ | ||
| 2347 | case 7: return "7"; /* 7xy: Tremolo */ | ||
| 2348 | case 8: return "8"; /* 8xx: Set panning */ | ||
| 2349 | case 9: return "9"; /* 9xx: Sample offset */ | ||
| 2350 | case 0xA: return "A";/* Axy: Volume slide */ | ||
| 2351 | case 0xB: return "B";/* Bxx: Position jump */ | ||
| 2352 | case 0xC: return "C";/* Cxx: Set volume */ | ||
| 2353 | case 0xD: return "D";/* Dxx: Pattern break */ | ||
| 2354 | case 0xE: return "E";/* EXy: Extended command */ | ||
| 2355 | case 0xF: return "F";/* Fxx: Set tempo/BPM */ | ||
| 2356 | case 16: return "G"; /* Gxx: Set global volume */ | ||
| 2357 | case 17: return "H"; /* Hxy: Global volume slide */ | ||
| 2358 | case 21: return "L"; /* Lxx: Set envelope position */ | ||
| 2359 | case 25: return "P"; /* Pxy: Panning slide */ | ||
| 2360 | case 27: return "R"; /* Rxy: Multi retrig note */ | ||
| 2361 | case 29: return "T"; /* Txy: Tremor */ | ||
| 2362 | case 33: return "X"; /* Xxy: Extra stuff */ | ||
| 2363 | default: return "?"; /* UNKNOWN */ | ||
| 2364 | }; | ||
| 2365 | } | ||
| 2366 | |||
| 2367 | #ifdef JAR_XM_RAYLIB | ||
| 2368 | |||
| 2369 | #include "raylib.h" // Need RayLib API calls for the DEBUG display | ||
| 2370 | |||
| 2371 | void jar_xm_debug(jar_xm_context_t *ctx) { | ||
| 2372 | int size=40; | ||
| 2373 | int x = 0, y = 0; | ||
| 2374 | |||
| 2375 | // DEBUG VARIABLES | ||
| 2376 | y += size; DrawText(TextFormat("CUR TBL = %i", ctx->current_table_index), x, y, size, WHITE); | ||
| 2377 | y += size; DrawText(TextFormat("CUR PAT = %i", ctx->module.pattern_table[ctx->current_table_index]), x, y, size, WHITE); | ||
| 2378 | y += size; DrawText(TextFormat("POS JMP = %d", ctx->position_jump), x, y, size, WHITE); | ||
| 2379 | y += size; DrawText(TextFormat("JMP DST = %i", ctx->jump_dest), x, y, size, WHITE); | ||
| 2380 | y += size; DrawText(TextFormat("PTN BRK = %d", ctx->pattern_break), x, y, size, WHITE); | ||
| 2381 | y += size; DrawText(TextFormat("CUR ROW = %i", ctx->current_row), x, y, size, WHITE); | ||
| 2382 | y += size; DrawText(TextFormat("JMP ROW = %i", ctx->jump_row), x, y, size, WHITE); | ||
| 2383 | y += size; DrawText(TextFormat("ROW LCT = %i", ctx->row_loop_count), x, y, size, WHITE); | ||
| 2384 | y += size; DrawText(TextFormat("LCT = %i", ctx->loop_count), x, y, size, WHITE); | ||
| 2385 | y += size; DrawText(TextFormat("MAX LCT = %i", ctx->max_loop_count), x, y, size, WHITE); | ||
| 2386 | x = size * 12; y = 0; | ||
| 2387 | |||
| 2388 | y += size; DrawText(TextFormat("CUR TCK = %i", ctx->current_tick), x, y, size, WHITE); | ||
| 2389 | y += size; DrawText(TextFormat("XTR TCK = %i", ctx->extra_ticks), x, y, size, WHITE); | ||
| 2390 | y += size; DrawText(TextFormat("TCK/ROW = %i", ctx->tempo), x, y, size, ORANGE); | ||
| 2391 | y += size; DrawText(TextFormat("SPL TCK = %f", ctx->remaining_samples_in_tick), x, y, size, WHITE); | ||
| 2392 | y += size; DrawText(TextFormat("GEN SPL = %i", ctx->generated_samples), x, y, size, WHITE); | ||
| 2393 | y += size * 7; | ||
| 2394 | |||
| 2395 | x = 0; | ||
| 2396 | size=16; | ||
| 2397 | // TIMELINE OF MODULE | ||
| 2398 | for (int i=0; i < ctx->module.length; i++) { | ||
| 2399 | if (i == ctx->jump_dest) { | ||
| 2400 | if (ctx->position_jump) { | ||
| 2401 | DrawRectangle(i * size * 2, y - size, size * 2, size, GOLD); | ||
| 2402 | } else { | ||
| 2403 | DrawRectangle(i * size * 2, y - size, size * 2, size, BROWN); | ||
| 2404 | }; | ||
| 2405 | }; | ||
| 2406 | if (i == ctx->current_table_index) { | ||
| 2407 | // DrawText(TextFormat("%02X", ctx->current_tick), i * size * 2, y - size, size, WHITE); | ||
| 2408 | DrawRectangle(i * size * 2, y, size * 2, size, RED); | ||
| 2409 | DrawText(TextFormat("%02X", ctx->current_row), i * size * 2, y - size, size, YELLOW); | ||
| 2410 | } else { | ||
| 2411 | DrawRectangle(i * size * 2, y, size * 2, size, ORANGE); | ||
| 2412 | }; | ||
| 2413 | DrawText(TextFormat("%02X", ctx->module.pattern_table[i]), i * size * 2, y, size, WHITE); | ||
| 2414 | }; | ||
| 2415 | y += size; | ||
| 2416 | |||
| 2417 | jar_xm_pattern_t* cur = ctx->module.patterns + ctx->module.pattern_table[ctx->current_table_index]; | ||
| 2418 | |||
| 2419 | /* DISPLAY CURRENTLY PLAYING PATTERN */ | ||
| 2420 | |||
| 2421 | x += 2 * size; | ||
| 2422 | for(uint8_t i = 0; i < ctx->module.num_channels; i++) { | ||
| 2423 | DrawRectangle(x, y, 8 * size, size, PURPLE); | ||
| 2424 | DrawText("N", x, y, size, YELLOW); | ||
| 2425 | DrawText("I", x + size * 2, y, size, YELLOW); | ||
| 2426 | DrawText("V", x + size * 4, y, size, YELLOW); | ||
| 2427 | DrawText("FX", x + size * 6, y, size, YELLOW); | ||
| 2428 | x += 9 * size; | ||
| 2429 | }; | ||
| 2430 | x += size; | ||
| 2431 | for (int j=(ctx->current_row - 14); j<(ctx->current_row + 15); j++) { | ||
| 2432 | y += size; | ||
| 2433 | x = 0; | ||
| 2434 | if (j >=0 && j < (cur->num_rows)) { | ||
| 2435 | DrawRectangle(x, y, size * 2, size, BROWN); | ||
| 2436 | DrawText(TextFormat("%02X",j), x, y, size, WHITE); | ||
| 2437 | x += 2 * size; | ||
| 2438 | for(uint8_t i = 0; i < ctx->module.num_channels; i++) { | ||
| 2439 | if (j==(ctx->current_row)) { | ||
| 2440 | DrawRectangle(x, y, 8 * size, size, DARKGREEN); | ||
| 2441 | } else { | ||
| 2442 | DrawRectangle(x, y, 8 * size, size, DARKGRAY); | ||
| 2443 | }; | ||
| 2444 | jar_xm_pattern_slot_t *s = cur->slots + j * ctx->module.num_channels + i; | ||
| 2445 | // jar_xm_channel_context_t *ch = ctx->channels + i; | ||
| 2446 | if (s->note > 0) {DrawText(TextFormat("%s%s", xm_note_chr(s->note), xm_octave_chr(s->note) ), x, y, size, WHITE);} else {DrawText("...", x, y, size, GRAY);}; | ||
| 2447 | if (s->instrument > 0) { | ||
| 2448 | DrawText(TextFormat("%02X", s->instrument), x + size * 2, y, size, WHITE); | ||
| 2449 | if (s->volume_column == 0) { | ||
| 2450 | DrawText(TextFormat("%02X", 64), x + size * 4, y, size, YELLOW); | ||
| 2451 | }; | ||
| 2452 | } else { | ||
| 2453 | DrawText("..", x + size * 2, y, size, GRAY); | ||
| 2454 | if (s->volume_column == 0) { | ||
| 2455 | DrawText("..", x + size * 4, y, size, GRAY); | ||
| 2456 | }; | ||
| 2457 | }; | ||
| 2458 | if (s->volume_column > 0) {DrawText(TextFormat("%02X", (s->volume_column - 16)), x + size * 4, y, size, WHITE);}; | ||
| 2459 | if (s->effect_type > 0 || s->effect_param > 0) {DrawText(TextFormat("%s%02X", xm_effect_chr(s->effect_type), s->effect_param), x + size * 6, y, size, WHITE);}; | ||
| 2460 | x += 9 * size; | ||
| 2461 | }; | ||
| 2462 | }; | ||
| 2463 | }; | ||
| 2464 | |||
| 2465 | } | ||
| 2466 | #endif // RayLib extension | ||
| 2467 | |||
| 2468 | #endif//end of JAR_XM_IMPLEMENTATION | ||
| 2469 | //------------------------------------------------------------------------------- | ||
| 2470 | |||
| 2471 | #endif//end of INCLUDE_JAR_XM_H | ||
