1 /*******************************************************************************
2 
3 MIT License
4 
5 Copyright (c) 2018 rombankzero
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 *******************************************************************************/
26 
27 // Translated to D by Guillaume Piolat Copyright 2021.
28 module audioformats.pocketmod;
29 
30 
31 version = POCKETMOD_NO_INTERPOLATION; // sounds better to me, linear interp is darker
32 nothrow:
33 @nogc:
34 
35 enum POCKETMOD_MAX_CHANNELS = 32;
36 enum POCKETMOD_MAX_SAMPLES = 31;
37 
38 struct _pocketmod_sample
39 {
40     byte* data;         /* Sample data buffer                      */
41     uint length;        /* Data length (in bytes)                  */
42 }
43 
44 struct _pocketmod_chan
45 {
46     ubyte dirty;        /* Pitch/volume dirty flags                */
47     ubyte sample;       /* Sample number (0..31)                   */
48     ubyte volume;       /* Base volume without tremolo (0..64)     */
49     ubyte balance;      /* Stereo balance (0..255)                 */
50     ushort period;      /* Note period (113..856)                  */
51     ushort delayed;     /* Delayed note period (113..856)          */
52     ushort target;      /* Target period (for tone portamento)     */
53     ubyte finetune;     /* Note finetune (0..15)                   */
54     ubyte loop_count;   /* E6x loop counter                        */
55     ubyte loop_line;    /* E6x target line                         */
56     ubyte lfo_step;     /* Vibrato/tremolo LFO step counter        */
57     ubyte[2] lfo_type;  /* LFO type for vibrato/tremolo            */
58     ubyte effect;       /* Current effect (0x0..0xf or 0xe0..0xef) */
59     ubyte param;        /* Raw effect parameter value              */
60     ubyte param3;       /* Parameter memory for 3xx                */
61     ubyte param4;       /* Parameter memory for 4xy                */
62     ubyte param7;       /* Parameter memory for 7xy                */
63     ubyte param9;       /* Parameter memory for 9xx                */
64     ubyte paramE1;      /* Parameter memory for E1x                */
65     ubyte paramE2;      /* Parameter memory for E2x                */
66     ubyte paramEA;      /* Parameter memory for EAx                */
67     ubyte paramEB;      /* Parameter memory for EBx                */
68     ubyte real_volume;  /* Volume (with tremolo adjustment)        */
69     float position;             /* Position in sample data buffer          */
70     float increment;            /* Position increment per output sample    */
71 }
72 
73 struct pocketmod_context
74 {
75     /* Read-only song data */
76     _pocketmod_sample[POCKETMOD_MAX_SAMPLES] samples;
77     ubyte *source;      /* Pointer to source MOD data              */
78     ubyte *order;       /* Pattern order table                     */
79     ubyte *patterns;    /* Start of pattern data                   */
80     ubyte length;       /* Patterns in the order (1..128)          */
81     ubyte reset;        /* Pattern to loop back to (0..127)        */
82     ubyte num_patterns; /* Patterns in the file (1..128)           */
83     ubyte num_samples;  /* Sample count (15 or 31)                 */
84     ubyte num_channels; /* Channel count (1..32)                   */
85 
86     /* Timing variables */
87     int samples_per_second;     /* Sample rate (set by user)               */
88     int ticks_per_line;         /* A.K.A. song speed (initially 6)         */
89     float samples_per_tick;     /* Depends on sample rate and BPM          */
90 
91     /* Loop detection state */
92     ubyte[16] visited;  /* Bit mask of previously visited patterns */
93     int loop_count;             /* How many times the song has looped      */
94 
95     /* Render state */
96     _pocketmod_chan[POCKETMOD_MAX_CHANNELS] channels;
97     ubyte pattern_delay;/* EEx pattern delay counter               */
98     uint lfo_rng;       /* RNG used for the random LFO waveform    */
99 
100     /* Position in song (from least to most granular) */
101     byte pattern;        /* Current pattern in order                */
102     byte line;           /* Current line in pattern                 */
103     short tick;                 /* Current tick in line                    */
104     float sample;               /* Current sample in tick                  */
105 }
106 
107 /* Memorize a parameter unless the new value is zero */
108 void POCKETMOD_MEM_ubyte(ref ubyte dst, ubyte src)
109 {
110     dst = src ? src : dst;
111 }
112 
113 void POCKETMOD_MEM_ushort(ref ushort dst, ushort src)
114 {
115     dst = src ? src : dst;
116 }
117 
118 void POCKETMOD_MEM2(ref ubyte dst, ubyte src)
119 {
120     dst = ((src & 0x0f) ? (src & 0x0f) : (dst & 0x0f)) 
121             |   ((src & 0xf0) ? (src & 0xf0) : (dst & 0xf0));
122 }
123 
124 /* Shortcut to sample metadata (sample must be nonzero) */
125 ubyte* POCKETMOD_SAMPLE(pocketmod_context *c, int sample)
126 {
127     return ((c).source + 12 + 30 * (sample));
128 }
129 
130 /* Channel dirty flags */
131 enum POCKETMOD_PITCH = 0x01;
132 enum POCKETMOD_VOLUME = 0x02;
133 
134 /* The size of one sample in bytes */
135 enum int POCKETMOD_SAMPLE_SIZE = 8;
136 
137 /* Finetune adjustment table. Three octaves for each finetune setting. */
138 static immutable byte[36][16] _pocketmod_finetune = [
139     [  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
140     [ -6, -6, -5, -5, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -3, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  0],
141     [-12,-12,-10,-11, -8, -8, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -4, -4, -4, -3, -3, -3, -3, -2, -3, -3, -2, -3, -3, -2, -2, -2, -2, -2, -2, -1],
142     [-18,-17,-16,-16,-13,-12,-12,-11,-10,-10,-10, -9, -9, -9, -8, -8, -7, -6, -6, -5, -5, -5, -5, -4, -5, -4, -3, -4, -4, -3, -3, -3, -3, -2, -2, -2],
143     [-24,-23,-21,-21,-18,-17,-16,-15,-14,-13,-13,-12,-12,-12,-11,-10, -9, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3],
144     [-30,-29,-26,-26,-23,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-13,-11,-11,-10, -9, -9, -9, -8, -7, -8, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4],
145     [-36,-34,-32,-31,-27,-26,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-11,-10,-10, -9, -9, -9, -7, -8, -7, -6, -6, -6, -6, -5, -5, -4],
146     [-42,-40,-37,-36,-32,-30,-29,-27,-25,-24,-23,-22,-21,-20,-18,-18,-16,-15,-14,-13,-13,-12,-12,-10,-10,-10, -9, -9, -9, -8, -7, -7, -7, -6, -6, -5],
147     [ 51, 48, 46, 42, 42, 38, 36, 34, 32, 30, 24, 27, 25, 24, 23, 21, 21, 19, 18, 17, 16, 15, 14, 14, 12, 12, 12, 10, 10, 10,  9,  8,  8,  8,  7,  7],
148     [ 44, 42, 40, 37, 37, 35, 32, 31, 29, 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 15, 14, 13, 12, 11, 10, 10,  9,  9,  9,  8,  7,  7,  7,  6,  6],
149     [ 38, 36, 34, 32, 31, 30, 28, 27, 25, 24, 22, 21, 19, 18, 17, 16, 16, 15, 14, 13, 13, 12, 11, 11,  9,  9,  9,  8,  7,  7,  7,  6,  6,  6,  5,  5],
150     [ 31, 30, 29, 26, 26, 25, 24, 22, 21, 20, 18, 17, 16, 15, 14, 13, 13, 12, 12, 11, 11, 10,  9,  9,  8,  7,  8,  7,  6,  6,  6,  5,  5,  5,  5,  5],
151     [ 25, 24, 23, 21, 21, 20, 19, 18, 17, 16, 14, 14, 13, 12, 11, 10, 11, 10, 10,  9,  9,  8,  7,  7,  6,  6,  6,  5,  5,  5,  5,  4,  4,  4,  3,  4],
152     [ 19, 18, 17, 16, 16, 15, 15, 14, 13, 12, 11, 10,  9,  9,  9,  8,  8, 18,  7,  7,  7,  6,  5,  6,  5,  4,  5,  4,  4,  4,  4,  3,  3,  3,  3,  3],
153     [ 12, 12, 12, 10, 11, 11, 10, 10,  9,  8,  7,  7,  6,  6,  6,  5,  6,  5,  5,  5,  5,  4,  4,  4,  3,  3,  3,  3,  2,  3,  3,  2,  2,  2,  2,  2],
154     [  6,  6,  6,  5,  6,  6,  6,  5,  5,  5,  4,  4,  3,  3,  3,  3,  3,  3,  3,  3,  3,  2,  2,  2,  2,  1,  2,  1,  1,  1,  1,  1,  1,  1,  1,  1]
155 ];
156 
157 /* Min/max helper functions */
158 int _pocketmod_min(int x, int y) 
159 { 
160    return x < y ? x : y; 
161 }
162 
163 int _pocketmod_max(int x, int y) 
164 { 
165     return x > y ? x : y; 
166 }
167 
168 /* Clamp a volume value to the 0..64 range */
169 int _pocketmod_clamp_volume(int x)
170 {
171     x = _pocketmod_max(x, 0x00);
172     x = _pocketmod_min(x, 0x40);
173     return x;
174 }
175 
176 /* Zero out a block of memory */
177 void _pocketmod_zero(void *data, size_t size)
178 {
179     char* byte_ = cast(char*) data;
180     char* end   = byte_ + size;
181     while (byte_ != end) { *byte_++ = 0; }
182 }
183 
184 /* Convert a period (at finetune = 0) to a note index in 0..35 */
185 int _pocketmod_period_to_note(int period)
186 {
187     switch (period) {
188         case 856: return  0; case 808: return  1; case 762: return  2;
189         case 720: return  3; case 678: return  4; case 640: return  5;
190         case 604: return  6; case 570: return  7; case 538: return  8;
191         case 508: return  9; case 480: return 10; case 453: return 11;
192         case 428: return 12; case 404: return 13; case 381: return 14;
193         case 360: return 15; case 339: return 16; case 320: return 17;
194         case 302: return 18; case 285: return 19; case 269: return 20;
195         case 254: return 21; case 240: return 22; case 226: return 23;
196         case 214: return 24; case 202: return 25; case 190: return 26;
197         case 180: return 27; case 170: return 28; case 160: return 29;
198         case 151: return 30; case 143: return 31; case 135: return 32;
199         case 127: return 33; case 120: return 34; case 113: return 35;
200         default: return 0;
201     }
202 }
203 
204 /* Table-based sine wave oscillator */
205 int _pocketmod_sin(int step)
206 {
207     /* round(sin(x * pi / 32) * 255) for x in 0..15 */
208     static immutable ubyte[16] sin = [
209         0x00, 0x19, 0x32, 0x4a, 0x62, 0x78, 0x8e, 0xa2,
210         0xb4, 0xc5, 0xd4, 0xe0, 0xec, 0xf4, 0xfa, 0xfe
211     ];
212     int x = sin[step & 0x0f];
213     x = (step & 0x1f) < 0x10 ? x : 0xff - x;
214     return step < 0x20 ? x : -x;
215 }
216 
217 /* Oscillators for vibrato/tremolo effects */
218 int _pocketmod_lfo(pocketmod_context *c, _pocketmod_chan *ch, int step)
219 {
220     switch (ch.lfo_type[ch.effect == 7] & 3) {
221         case 0: return _pocketmod_sin(step & 0x3f);         /* Sine   */
222         case 1: return 0xff - ((step & 0x3f) << 3);         /* Saw    */
223         case 2: return (step & 0x3f) < 0x20 ? 0xff : -0xff; /* Square */
224         case 3: return (c.lfo_rng & 0x1ff) - 0xff;         /* Random */
225         default: return 0; /* Hush little compiler */
226     }
227 }
228 
229 void _pocketmod_update_pitch(pocketmod_context *c, _pocketmod_chan *ch)
230 {
231     /* Don't do anything if the period is zero */
232     ch.increment = 0.0f;
233     if (ch.period) {
234         float period = ch.period;
235 
236         /* Apply vibrato (if active) */
237         if (ch.effect == 0x4 || ch.effect == 0x6) {
238             int step = (ch.param4 >> 4) * ch.lfo_step;
239             int rate = ch.param4 & 0x0f;
240             period += _pocketmod_lfo(c, ch, step) * rate / 128.0f;
241 
242         /* Apply arpeggio (if active) */
243         } else if (ch.effect == 0x0 && ch.param) {
244             static immutable float[16] arpeggio = [ /* 2^(X/12) for X in 0..15 */
245                 1.000000f, 1.059463f, 1.122462f, 1.189207f,
246                 1.259921f, 1.334840f, 1.414214f, 1.498307f,
247                 1.587401f, 1.681793f, 1.781797f, 1.887749f,
248                 2.000000f, 2.118926f, 2.244924f, 2.378414f
249             ];
250             int step = (ch.param >> ((2 - c.tick % 3) << 2)) & 0x0f;
251             period /= arpeggio[step];
252         }
253 
254         /* Calculate sample buffer position increment */
255         ch.increment = 3546894.6f / (period * c.samples_per_second);
256     }
257 
258     /* Clear the pitch dirty flag */
259     ch.dirty &= ~POCKETMOD_PITCH;
260 }
261 
262 void _pocketmod_update_volume(pocketmod_context *c, _pocketmod_chan *ch)
263 {
264     int volume = ch.volume;
265     if (ch.effect == 0x7) {
266         int step = ch.lfo_step * (ch.param7 >> 4);
267         volume += _pocketmod_lfo(c, ch, step) * (ch.param7 & 0x0f) >> 6;
268     }
269     ch.real_volume = cast(ubyte) _pocketmod_clamp_volume(volume);
270     ch.dirty &= ~POCKETMOD_VOLUME;
271 }
272 
273 void _pocketmod_pitch_slide(_pocketmod_chan *ch, int amount)
274 {
275     int max = 856 + _pocketmod_finetune[ch.finetune][ 0];
276     int min = 113 + _pocketmod_finetune[ch.finetune][35];
277     ch.period += amount;
278     ch.period = cast(ushort) _pocketmod_max(ch.period, min);
279     ch.period = cast(ushort) _pocketmod_min(ch.period, max);
280     ch.dirty |= POCKETMOD_PITCH;
281 }
282 
283 void _pocketmod_volume_slide(_pocketmod_chan *ch, int param)
284 {
285     /* Undocumented quirk: If both x and y are nonzero, then the value of x */
286     /* takes precedence. (Yes, there are songs that rely on this behavior.) */
287     int change = (param & 0xf0) ? (param >> 4) : -(param & 0x0f);
288     ch.volume = cast(ubyte) _pocketmod_clamp_volume(ch.volume + change);
289     ch.dirty |= POCKETMOD_VOLUME;
290 }
291 
292 void _pocketmod_next_line(pocketmod_context *c)
293 {
294     ubyte[4]* data;
295     int i, pos, pattern_break = -1;
296 
297     /* When entering a new pattern order index, mark it as "visited" */
298     if (c.line == 0) 
299     {
300         c.visited[c.pattern >> 3] |= 1 << (c.pattern & 7);
301     }
302 
303     /* Move to the next pattern if this was the last line */
304     if (++c.line == 64) 
305     {
306         if (++c.pattern == c.length) 
307         {
308             c.pattern = c.reset;
309         }
310         c.line = 0;
311     }
312 
313     /* Find the pattern data for the current line */
314     pos = (c.order[c.pattern] * 64 + c.line) * c.num_channels * 4;
315     data = cast(ubyte[4]*) (c.patterns + pos);
316     for (i = 0; i < c.num_channels; i++) 
317     {
318 
319         /* Decode columns */
320         int sample = (data[i][0] & 0xf0) | (data[i][2] >> 4);
321         int period = ((data[i][0] & 0x0f) << 8) | data[i][1];
322         int effect = ((data[i][2] & 0x0f) << 8) | data[i][3];
323 
324         /* Memorize effect parameter values */
325         _pocketmod_chan *ch = &c.channels[i];
326         ch.effect = cast(ubyte) ( (effect >> 8) != 0xe ? (effect >> 8) : (effect >> 4) );
327         ch.param = (effect >> 8) != 0xe ? (effect & 0xff) : (effect & 0x0f);
328 
329         /* Set sample */
330         if (sample) {
331             if (sample <= POCKETMOD_MAX_SAMPLES) {
332                 ubyte *sample_data = POCKETMOD_SAMPLE(c, sample);
333                 ch.sample = cast(ubyte)sample;
334                 ch.finetune = sample_data[2] & 0x0f;
335                 ch.volume = cast(ubyte)_pocketmod_min(sample_data[3], 0x40);
336                 if (ch.effect != 0xED) {
337                     ch.dirty |= POCKETMOD_VOLUME;
338                 }
339             } else {
340                 ch.sample = 0;
341             }
342         }
343 
344         /* Set note */
345         if (period) {
346             int note = _pocketmod_period_to_note(period);
347             period += _pocketmod_finetune[ch.finetune][note];
348             if (ch.effect != 0x3) {
349                 if (ch.effect != 0xED) {
350                     ch.period = cast(ushort) period;
351                     ch.dirty |= POCKETMOD_PITCH;
352                     ch.position = 0.0f;
353                     ch.lfo_step = 0;
354                 } else {
355                     ch.delayed = cast(ushort) period;
356                 }
357             }
358         }
359 
360         /* Handle pattern effects */
361         switch (ch.effect) {
362 
363             /* Memorize parameters */
364             case 0x3: POCKETMOD_MEM_ubyte(ch.param3, ch.param); goto case 0x5; /* Fall through */
365             case 0x5: POCKETMOD_MEM_ushort(ch.target, cast(ushort)period); break;
366             case 0x4: POCKETMOD_MEM2(ch.param4, ch.param); break;
367             case 0x7: POCKETMOD_MEM2(ch.param7, ch.param); break;
368             case 0xE1: POCKETMOD_MEM_ubyte(ch.paramE1, ch.param); break;
369             case 0xE2: POCKETMOD_MEM_ubyte(ch.paramE2, ch.param); break;
370             case 0xEA: POCKETMOD_MEM_ubyte(ch.paramEA, ch.param); break;
371             case 0xEB: POCKETMOD_MEM_ubyte(ch.paramEB, ch.param); break;
372 
373             /* 8xx: Set stereo balance (nonstandard) */
374             case 0x8: {
375                 ch.balance = ch.param;
376             } break;
377 
378             /* 9xx: Set sample offset */
379             case 0x9: {
380                 if (period != 0 || sample != 0) {
381                     ch.param9 = ch.param ? ch.param : ch.param9;
382                     ch.position = ch.param9 << 8;
383                 }
384             } break;
385 
386             /* Bxx: Jump to pattern */
387             case 0xB: {
388                 c.pattern = ch.param < c.length ? ch.param : 0;
389                 c.line = -1;
390             } break;
391 
392             /* Cxx: Set volume */
393             case 0xC: {
394                 ch.volume = cast(ubyte)_pocketmod_clamp_volume(ch.param);
395                 ch.dirty |= POCKETMOD_VOLUME;
396             } break;
397 
398             /* Dxy: Pattern break */
399             case 0xD: {
400                 pattern_break = (ch.param >> 4) * 10 + (ch.param & 15);
401             } break;
402 
403             /* E4x: Set vibrato waveform */
404             case 0xE4: {
405                 ch.lfo_type[0] = ch.param;
406             } break;
407 
408             /* E5x: Set sample finetune */
409             case 0xE5: {
410                 ch.finetune = ch.param;
411                 ch.dirty |= POCKETMOD_PITCH;
412             } break;
413 
414             /* E6x: Pattern loop */
415             case 0xE6: {
416                 if (ch.param) {
417                     if (!ch.loop_count) {
418                         ch.loop_count = ch.param;
419                         c.line = ch.loop_line;
420                     } else if (--ch.loop_count) {
421                         c.line = ch.loop_line;
422                     }
423                 } else {
424                     ch.loop_line = cast(ubyte)(c.line - 1);
425                 }
426             } break;
427 
428             /* E7x: Set tremolo waveform */
429             case 0xE7: {
430                 ch.lfo_type[1] = ch.param;
431             } break;
432 
433             /* E8x: Set stereo balance (nonstandard) */
434             case 0xE8: {
435                 ch.balance = cast(ubyte)(ch.param << 4);
436             } break;
437 
438             /* EEx: Pattern delay */
439             case 0xEE: {
440                 c.pattern_delay = ch.param;
441             } break;
442 
443             /* Fxx: Set speed */
444             case 0xF: {
445                 if (ch.param != 0) {
446                     if (ch.param < 0x20) {
447                         c.ticks_per_line = ch.param;
448                     } else {
449                         float rate = c.samples_per_second;
450                         c.samples_per_tick = rate / (0.4f * ch.param);
451                     }
452                 }
453             } break;
454 
455             default: break;
456         }
457     }
458 
459     /* Pattern breaks are handled here, so that only one jump happens even  */
460     /* when multiple Dxy commands appear on the same line. (You guessed it: */
461     /* There are songs that rely on this behavior!)                         */
462     if (pattern_break != -1) {
463         c.line = cast(byte)( (pattern_break < 64 ? pattern_break : 0) - 1 );
464         if (++c.pattern == c.length) {
465             c.pattern = c.reset;
466         }
467     }
468 }
469 
470 void _pocketmod_next_tick(pocketmod_context *c)
471 {
472     int i;
473 
474     /* Move to the next line if this was the last tick */
475     if (++c.tick == c.ticks_per_line) {
476         if (c.pattern_delay > 0) {
477             c.pattern_delay--;
478         } else {
479             _pocketmod_next_line(c);
480         }
481         c.tick = 0;
482     }
483 
484     /* Make per-tick adjustments for all channels */
485     for (i = 0; i < c.num_channels; i++) {
486         _pocketmod_chan *ch = &c.channels[i];
487         int param = ch.param;
488 
489         /* Advance the LFO random number generator */
490         c.lfo_rng = 0x0019660d * c.lfo_rng + 0x3c6ef35f;
491 
492         /* Handle effects that may happen on any tick of a line */
493         switch (ch.effect) {
494 
495             /* 0xy: Arpeggio */
496             case 0x0: {
497                 ch.dirty |= POCKETMOD_PITCH;
498             } break;
499 
500             /* E9x: Retrigger note every x ticks */
501             case 0xE9: {
502                 if (!(param && c.tick % param)) {
503                     ch.position = 0.0f;
504                     ch.lfo_step = 0;
505                 }
506             } break;
507 
508             /* ECx: Cut note after x ticks */
509             case 0xEC: {
510                 if (c.tick == param) {
511                     ch.volume = 0;
512                     ch.dirty |= POCKETMOD_VOLUME;
513                 }
514             } break;
515 
516             /* EDx: Delay note for x ticks */
517             case 0xED: {
518                 if (c.tick == param && ch.sample) {
519                     ch.dirty |= POCKETMOD_VOLUME | POCKETMOD_PITCH;
520                     ch.period = ch.delayed;
521                     ch.position = 0.0f;
522                     ch.lfo_step = 0;
523                 }
524             } break;
525 
526             default: break;
527         }
528 
529         /* Handle effects that only happen on the first tick of a line */
530         if (c.tick == 0) {
531             switch (ch.effect) {
532                 case 0xE1: _pocketmod_pitch_slide(ch, -cast(int)ch.paramE1); break;
533                 case 0xE2: _pocketmod_pitch_slide(ch, cast(int)ch.paramE2); break;
534                 case 0xEA: _pocketmod_volume_slide(ch, ch.paramEA << 4); break;
535                 case 0xEB: _pocketmod_volume_slide(ch, ch.paramEB & 15); break;
536                 default: break;
537             }
538 
539         /* Handle effects that are not applied on the first tick of a line */
540         } else {
541             switch (ch.effect) {
542 
543                 /* 1xx: Portamento up */
544                 case 0x1: {
545                     _pocketmod_pitch_slide(ch, -param);
546                 } break;
547 
548                 /* 2xx: Portamento down */
549                 case 0x2: {
550                     _pocketmod_pitch_slide(ch, +param);
551                 } break;
552 
553                 /* 5xy: Volume slide + tone portamento */
554                 case 0x5: {
555                     _pocketmod_volume_slide(ch, param);
556                     goto case 0x3;
557                 }
558 
559                 /* 3xx: Tone portamento */
560                 case 0x3: {
561                     int rate = ch.param3;
562                     int order = ch.period < ch.target;
563                     int closer = ch.period + (order ? rate : -rate);
564                     int new_order = closer < ch.target;
565                     ch.period = cast(ushort)(new_order == order ? closer : ch.target);
566                     ch.dirty |= POCKETMOD_PITCH;
567                 } break;
568 
569                 /* 6xy: Volume slide + vibrato */
570                 case 0x6: {
571                     _pocketmod_volume_slide(ch, param);
572                     goto case 0x4;
573                 }
574 
575                 /* 4xy: Vibrato */
576                 case 0x4: {
577                     ch.lfo_step++;
578                     ch.dirty |= POCKETMOD_PITCH;
579                 } break;
580 
581                 /* 7xy: Tremolo */
582                 case 0x7: {
583                     ch.lfo_step++;
584                     ch.dirty |= POCKETMOD_VOLUME;
585                 } break;
586 
587                 /* Axy: Volume slide */
588                 case 0xA: {
589                     _pocketmod_volume_slide(ch, param);
590                 } break;
591 
592                 default: break;
593             }
594         }
595 
596         /* Update channel volume/pitch if either is out of date */
597         if (ch.dirty & POCKETMOD_VOLUME) { _pocketmod_update_volume(c, ch); }
598         if (ch.dirty & POCKETMOD_PITCH) { _pocketmod_update_pitch(c, ch); }
599     }
600 }
601 
602 void _pocketmod_render_channel(pocketmod_context *c,
603                                _pocketmod_chan *chan,
604                                float *output,
605                                int samples_to_write)
606 {
607     /* Gather some loop data */
608     _pocketmod_sample *sample = &c.samples[chan.sample - 1];
609     ubyte *data = POCKETMOD_SAMPLE(c, chan.sample);
610     const int loop_start = ((data[4] << 8) | data[5]) << 1;
611     const int loop_length = ((data[6] << 8) | data[7]) << 1;
612     const int loop_end = loop_length > 2 ? loop_start + loop_length : 0xffffff;
613     const float sample_end = 1 + _pocketmod_min(loop_end, sample.length);
614 
615     /* Calculate left/right levels */
616     const float volume = chan.real_volume / cast(float) (128 * 64 * 4);
617     const float level_l = volume * (1.0f - chan.balance / 255.0f);
618     const float level_r = volume * (0.0f + chan.balance / 255.0f);
619 
620     /* Write samples */
621     int i, num;
622     do {
623 
624         /* Calculate how many samples we can write in one go */
625         num = cast(int)( (sample_end - chan.position) / chan.increment );
626         num = _pocketmod_min(num, samples_to_write);
627 
628         /* Resample and write 'num' samples */
629         for (i = 0; i < num; i++) 
630         {
631             int x0 = cast(int)(chan.position);
632             version(POCKETMOD_NO_INTERPOLATION)
633             {
634                 float s = sample.data[x0];
635             }
636             else
637             {
638                 int x1 = x0 + 1 - loop_length * (x0 + 1 >= loop_end);
639                 float t = chan.position - x0;
640                 float s = (1.0f - t) * sample.data[x0] + t * sample.data[x1];
641             }
642 
643             chan.position += chan.increment;
644             *output++ += level_l * s;
645             *output++ += level_r * s;
646         }
647 
648         /* Rewind the sample when reaching the loop point */
649         if (chan.position >= loop_end) 
650         {
651             chan.position -= loop_length;
652             /* Cut the sample if the end is reached */
653         } 
654         else if (chan.position >= sample.length) 
655         {
656             chan.position = -1.0f;
657             break;
658         }
659 
660         samples_to_write -= num;
661     } while (num > 0);
662 }
663 
664 // Modification: can be called without a context, for probing file format.
665 int _pocketmod_ident(pocketmod_context *c, ubyte *data, int size)
666 {
667     int i, j;
668 
669     /* 31-instrument files are at least 1084 bytes long */
670     if (size >= 1084) 
671     {
672 
673         /* The format tag is located at offset 1080 */
674         ubyte *tag = data + 1080;
675 
676         /* List of recognized format tags (possibly incomplete) */
677         static struct Format_Tag
678         {
679             char[5] name;
680             char channels;
681         }
682 
683         static immutable Format_Tag[40] tags =
684         [
685             /* TODO: FLT8 intentionally omitted because I haven't been able */
686             /* to find a specimen to test its funky pattern pairing format  */
687             Format_Tag("M.K.",  4), Format_Tag("M!K!",  4), Format_Tag("FLT4",  4), Format_Tag("4CHN",  4),
688             Format_Tag("OKTA",  8), Format_Tag("OCTA",  8), Format_Tag("CD81",  8), Format_Tag("FA08",  8),
689             Format_Tag("1CHN",  1), Format_Tag("2CHN",  2), Format_Tag("3CHN",  3), Format_Tag("4CHN",  4),
690             Format_Tag("5CHN",  5), Format_Tag("6CHN",  6), Format_Tag("7CHN",  7), Format_Tag("8CHN",  8),
691             Format_Tag("9CHN",  9), Format_Tag("10CH", 10), Format_Tag("11CH", 11), Format_Tag("12CH", 12),
692             Format_Tag("13CH", 13), Format_Tag("14CH", 14), Format_Tag("15CH", 15), Format_Tag("16CH", 16),
693             Format_Tag("17CH", 17), Format_Tag("18CH", 18), Format_Tag("19CH", 19), Format_Tag("20CH", 20),
694             Format_Tag("21CH", 21), Format_Tag("22CH", 22), Format_Tag("23CH", 23), Format_Tag("24CH", 24),
695             Format_Tag("25CH", 25), Format_Tag("26CH", 26), Format_Tag("27CH", 27), Format_Tag("28CH", 28),
696             Format_Tag("29CH", 29), Format_Tag("30CH", 30), Format_Tag("31CH", 31), Format_Tag("32CH", 32)
697         ];
698 
699         /* Check the format tag to determine if this is a 31-sample MOD */
700         for (i = 0; i < cast(int) (tags.length); i++) 
701         {
702             if (tags[i].name[0] == tag[0] && tags[i].name[1] == tag[1]
703              && tags[i].name[2] == tag[2] && tags[i].name[3] == tag[3]) 
704             {
705                 if (c)
706                 {
707                     c.num_channels = tags[i].channels;
708                     c.length = data[950];
709                     c.reset = data[951];
710                     c.order = &data[952];
711                     c.patterns = &data[1084];
712                     c.num_samples = 31;
713                 }
714                 return 1;
715             }
716         }
717     }
718 
719     /* A 15-instrument MOD has to be at least 600 bytes long */
720     if (size < 600) {
721         return 0;
722     }
723 
724     /* Check that the song title only contains ASCII bytes (or null) */
725     for (i = 0; i < 20; i++) {
726         if (data[i] != '\0' && (data[i] < ' ' || data[i] > '~')) {
727             return 0;
728         }
729     }
730 
731     /* Check that sample names only contain ASCII bytes (or null) */
732     for (i = 0; i < 15; i++) {
733         for (j = 0; j < 22; j++) {
734             char chr = data[20 + i * 30 + j];
735             if (chr != '\0' && (chr < ' ' || chr > '~')) {
736                 return 0;
737             }
738         }
739     }
740 
741     /* It looks like we have an older 15-instrument MOD */
742     if (c)
743     {
744         c.length = data[470];
745         c.reset = data[471];
746         c.order = &data[472];
747         c.patterns = &data[600];
748         c.num_samples = 15;
749         c.num_channels = 4;
750     }
751     return 1;
752 }
753 
754 int pocketmod_init(pocketmod_context *c, const void *data, int size, int rate)
755 {
756     int i, remaining, header_bytes, pattern_bytes;
757     ubyte*byte_ = cast(ubyte*) c;
758     byte *sample_data;
759 
760     /* Check that arguments look more or less sane */
761     if (!c || !data || rate <= 0 || size <= 0) {
762         return 0;
763     }
764 
765     /* Zero out the whole context and identify the MOD type */
766     _pocketmod_zero(c, pocketmod_context.sizeof);
767     c.source = cast(ubyte*) data;
768     if (!_pocketmod_ident(c, c.source, size)) {
769         return 0;
770     }
771 
772     /* Check that we are compiled with support for enough channels */
773     if (c.num_channels > POCKETMOD_MAX_CHANNELS) {
774         return 0;
775     }
776 
777     /* Check that we have enough sample slots for this file */
778     if (POCKETMOD_MAX_SAMPLES < 31) {
779         byte_ = cast(ubyte*) data + 20;
780         for (i = 0; i < c.num_samples; i++) {
781             uint length = 2 * ((byte_[22] << 8) | byte_[23]);
782             if (i >= POCKETMOD_MAX_SAMPLES && length > 2) {
783                 return 0; /* Can't fit this sample */
784             }
785             byte_ += 30;
786         }
787     }
788 
789     /* Check that the song length is in valid range (1..128) */
790     if (c.length == 0 || c.length > 128) {
791         return 0;
792     }
793 
794     /* Make sure that the reset pattern doesn't take us out of bounds */
795     if (c.reset >= c.length) {
796         c.reset = 0;
797     }
798 
799     /* Count how many patterns there are in the file */
800     c.num_patterns = 0;
801     for (i = 0; i < 128 && c.order[i] < 128; i++) {
802         c.num_patterns = cast(ubyte) _pocketmod_max(c.num_patterns, c.order[i]);
803     }
804     pattern_bytes = 256 * c.num_channels * ++c.num_patterns;
805     header_bytes = cast(int) (cast(char*) c.patterns - cast(char*) data);
806 
807     /* Check that each pattern in the order is within file bounds */
808     for (i = 0; i < c.length; i++) {
809         if (header_bytes + 256 * c.num_channels * c.order[i] > size) {
810             return 0; /* Reading this pattern would be a buffer over-read! */
811         }
812     }
813 
814     /* Check that the pattern data doesn't extend past the end of the file */
815     if (header_bytes + pattern_bytes > size) {
816         return 0;
817     }
818 
819     /* Load sample payload data, truncating ones that extend outside the file */
820     remaining = size - header_bytes - pattern_bytes;
821     sample_data = cast(byte*) data + header_bytes + pattern_bytes;
822     for (i = 0; i < c.num_samples; i++) 
823     {
824         ubyte *data2 = POCKETMOD_SAMPLE(c, i + 1);
825         uint length = ((data2[0] << 8) | data2[1]) << 1;
826         _pocketmod_sample *sample = &c.samples[i];
827         sample.data = sample_data;
828         sample.length = _pocketmod_min(length > 2 ? length : 0, remaining);
829         sample_data += sample.length;
830         remaining -= sample.length;
831     }
832 
833     /* Set up ProTracker default panning for all channels */
834     for (i = 0; i < c.num_channels; i++) {
835         c.channels[i].balance = 0x80 + ((((i + 1) >> 1) & 1) ? 0x20 : -0x20);
836     }
837 
838     /* Prepare to render from the start */
839     c.ticks_per_line = 6;
840     c.samples_per_second = rate;
841     c.samples_per_tick = rate / 50.0f;
842     c.lfo_rng = 0xbadc0de;
843     c.line = -1;
844     c.tick = cast(ushort)(c.ticks_per_line - 1);
845     _pocketmod_next_tick(c);
846     return 1;
847 }
848 
849 int pocketmod_render(pocketmod_context *c, void *buffer, int buffer_size)
850 {
851     int i, samples_rendered = 0;
852     int samples_remaining = buffer_size / POCKETMOD_SAMPLE_SIZE;
853     if (c && buffer) 
854     {
855         float[2]* output = cast(float[2]*) buffer;
856         while (samples_remaining > 0) {
857 
858             /* Calculate the number of samples left in this tick */
859             int num = cast(int) (c.samples_per_tick - c.sample);
860             num = _pocketmod_min(num + !num, samples_remaining);
861 
862             /* Render and mix 'num' samples from each channel */
863             _pocketmod_zero(output, num * POCKETMOD_SAMPLE_SIZE);
864             for (i = 0; i < c.num_channels; i++) {
865                 _pocketmod_chan *chan = &c.channels[i];
866                 if (chan.sample != 0 && chan.position >= 0.0f) {
867                     _pocketmod_render_channel(c, chan, (*output).ptr, num);
868                 }
869             }
870             samples_remaining -= num;
871             samples_rendered += num;
872             output += num;
873 
874             /* Advance song position by 'num' samples */
875             if ((c.sample += num) >= c.samples_per_tick) {
876                 c.sample -= c.samples_per_tick;
877                 _pocketmod_next_tick(c);
878 
879                 /* Stop if a new pattern was reached */
880                 if (c.line == 0 && c.tick == 0) {
881 
882                     /* Increment loop counter as needed */
883                     if (c.visited[c.pattern >> 3] & (1 << (c.pattern & 7))) {
884                         _pocketmod_zero(c.visited.ptr, (c.visited).sizeof);
885                         c.loop_count++;
886                     }
887                     break;
888                 }
889             }
890         }
891     }
892     return samples_rendered * POCKETMOD_SAMPLE_SIZE;
893 }
894 
895 int pocketmod_loop_count(pocketmod_context *c)
896 {
897     return c.loop_count;
898 }
899