The OpenD Programming Language

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