source: mainline/uspace/lib/trackmod/trackmod.c@ 8bf9058

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8bf9058 was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • Property mode set to 100644
File size: 23.3 KB
Line 
1/*
2 * Copyright (c) 2014 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup trackmod
30 * @{
31 */
32/**
33 * @file Tracker module handling library.
34 */
35
36#include <assert.h>
37#include <errno.h>
38#include <stdio.h>
39#include <stdlib.h>
40
41#include "macros.h"
42#include "protracker.h"
43#include "trackmod.h"
44#include "xm.h"
45
46/** Tunables */
47enum {
48 amp_factor = 16
49};
50
51/** Standard definitions set in stone */
52enum {
53 /** Base sample clock */
54 base_clock = 8363 * 428,
55 /** Maximum sample volume */
56 vol_max = 64,
57 /** Minimum period */
58 period_min = 113,
59 /** Maxium period */
60 period_max = 856
61};
62
63/** Table for finetune computation.
64 *
65 * Finetune is a number ft in [-8 .. 7]. The pitch should be adjusted by
66 * ft/8 semitones. To adjust pitch by 1/8 semitone down we can mutiply the
67 * period by 2^(1/12/8) =. 1.0072, one semitone up: 2^-(1/12/8) =. 0.9928,
68 * to adjust by ft/8 semitones, multiply by 2^(-ft/12/8).
69 *
70 * finetune_factor[ft] := 10000 * 2^(-ft/12/8)
71 * res_period = clip(period * fineture_factor[ft+8] / 10000)
72 */
73static unsigned finetune_factor[16] = {
74 10595, 10518, 10443, 10368, 10293, 10219, 10145, 10072,
75 10000, 9928, 9857, 9786, 9715, 9645, 9576, 9507
76};
77
78static unsigned period_table[12 * 8] = {
79 907, 900, 894, 887, 881, 875, 868, 862, 856, 850, 844, 838,
80 832, 826, 820, 814, 808, 802, 796, 791, 785, 779, 774, 768,
81 762, 757, 752, 746, 741, 736, 730, 725, 720, 715, 709, 704,
82 699, 694, 689, 684, 678, 675, 670, 665, 660, 655, 651, 646,
83 640, 636, 632, 628, 623, 619, 614, 610, 604, 601, 597, 592,
84 588, 584, 580, 575, 570, 567, 563, 559, 555, 551, 547, 543,
85 538, 535, 532, 528, 524, 520, 516, 513, 508, 505, 502, 498,
86 494, 491, 487, 484, 480, 477, 474, 470, 467, 463, 460, 457
87};
88
89static size_t trackmod_get_next_ord_idx(trackmod_modplay_t *);
90
91/** Destroy sample.
92 *
93 * @param sample Sample
94 */
95static void trackmod_sample_destroy(trackmod_sample_t *sample)
96{
97 free(sample->data);
98}
99
100/** Destroy instrument.
101 *
102 * @param instr Intrument
103 */
104static void trackmod_instr_destroy(trackmod_instr_t *instr)
105{
106 size_t i;
107
108 for (i = 0; i < instr->samples; i++)
109 trackmod_sample_destroy(&instr->sample[i]);
110}
111
112/** Destroy pattern.
113 *
114 * @param pattern Pattern
115 */
116static void trackmod_pattern_destroy(trackmod_pattern_t *pattern)
117{
118 free(pattern->data);
119}
120
121/** Create new empty module structure.
122 *
123 * @return New module
124 */
125trackmod_module_t *trackmod_module_new(void)
126{
127 return calloc(1, sizeof(trackmod_module_t));
128}
129
130/** Destroy module.
131 *
132 * @param module Module
133 */
134void trackmod_module_destroy(trackmod_module_t *module)
135{
136 size_t i;
137
138 /* Destroy samples */
139 if (module->instr != NULL) {
140 for (i = 0; i < module->instrs; i++)
141 trackmod_instr_destroy(&module->instr[i]);
142 free(module->instr);
143 }
144
145 /* Destroy patterns */
146 if (module->pattern != NULL) {
147 for (i = 0; i < module->patterns; i++)
148 trackmod_pattern_destroy(&module->pattern[i]);
149 free(module->pattern);
150 }
151
152 free(module->ord_list);
153 free(module);
154}
155
156errno_t trackmod_module_load(char *fname, trackmod_module_t **rmodule)
157{
158 errno_t rc;
159
160 rc = trackmod_xm_load(fname, rmodule);
161 if (rc == EOK)
162 return EOK;
163
164 rc = trackmod_protracker_load(fname, rmodule);
165 return rc;
166}
167
168/** Return current pattern.
169 *
170 * @param modplay Module playback
171 * @return Pattern
172 */
173static trackmod_pattern_t *trackmod_cur_pattern(trackmod_modplay_t *modplay)
174{
175 unsigned pat_idx;
176
177 pat_idx = modplay->module->ord_list[modplay->ord_idx];
178 return &modplay->module->pattern[pat_idx];
179}
180
181/** Decode pattern cell.
182 *
183 * @param pattern Pattern
184 * @param row Row number
185 * @param channel Channel number
186 * @param cell Place to store decoded cell
187 */
188static void trackmod_pattern_get_cell(trackmod_pattern_t *pattern,
189 size_t row, size_t channel, trackmod_cell_t *cell)
190{
191 *cell = pattern->data[row * pattern->channels + channel];
192}
193
194/** Compute floor(a / b), and the remainder.
195 *
196 * Unlike standard integer division this rounds towars negative infinity,
197 * not towards zero.
198 *
199 * @param a Dividend
200 * @param b Divisor
201 * @param quot Place to store 'quotient' (floor (a/b))
202 * @param rem Place to store 'remainder' (a - floor(a/b) * b)
203 */
204static void divmod_floor(int a, int b, int *quot, int *rem)
205{
206 if (b < 0) {
207 a = -a;
208 b = -b;
209 }
210
211 if (a >= 0) {
212 *quot = a / b;
213 *rem = a % b;
214 } else {
215 *quot = -(-a + (b - 1)) / b;
216 *rem = a - (*quot * b);
217 }
218}
219
220/** Process note (period)
221 *
222 * @param modplay Module playback
223 * @param i Channel number
224 * @param cell Cell
225 */
226static void trackmod_process_note(trackmod_modplay_t *modplay, size_t i,
227 trackmod_cell_t *cell)
228{
229 trackmod_chan_t *chan = &modplay->chan[i];
230 int period;
231 int pitch;
232 int octave;
233 int opitch;
234
235 if (chan->sample == NULL)
236 return;
237
238 if (cell->period == 0) {
239 pitch = 8 * (cell->note + chan->sample->rel_note) +
240 chan->sample->finetune;
241 divmod_floor(pitch, 8 * 12, &octave, &opitch);
242
243 if (octave >= 0)
244 period = period_table[opitch] * 8 / (1 << octave);
245 else
246 period = period_table[opitch] * 8 * (1 << (-octave));
247 } else {
248 period = cell->period;
249 period = period *
250 finetune_factor[chan->sample->finetune + 8] / 10000;
251 if (period > period_max)
252 period = period_max;
253 if (period < period_min)
254 period = period_min;
255 }
256
257 chan->period_new = period;
258}
259
260/** Process instrument number (this is what triggers the note playback)
261 *
262 * @param modplay Module playback
263 * @param i Channel number
264 * @param cell Cell
265 */
266static void trackmod_process_instr(trackmod_modplay_t *modplay, size_t i,
267 trackmod_cell_t *cell)
268{
269 trackmod_chan_t *chan = &modplay->chan[i];
270 trackmod_instr_t *instr;
271 size_t iidx;
272 size_t sidx;
273
274 if (cell->instr == 0)
275 return;
276
277 iidx = (cell->instr - 1) % modplay->module->instrs;
278 instr = &modplay->module->instr[iidx];
279 sidx = instr->key_smp[cell->note] % instr->samples;
280 chan->sample = &instr->sample[sidx];
281 chan->smp_pos = 0;
282 chan->lsmp = 0;
283
284 chan->volume = modplay->chan[i].sample->def_vol;
285}
286
287/** Process keyoff note
288 *
289 * @param modplay Module playback
290 * @param i Channel number
291 * @param cell Cell
292 */
293static void trackmod_process_keyoff_note(trackmod_modplay_t *modplay, size_t i)
294{
295 trackmod_chan_t *chan = &modplay->chan[i];
296
297 chan->sample = NULL;
298 chan->period = 0;
299 chan->smp_pos = 0;
300 chan->lsmp = 0;
301}
302
303/** Process Set volume effect.
304 *
305 * @param modplay Module playback
306 * @param chan Channel number
307 * @param param Effect parameter
308 */
309static void trackmod_effect_set_volume(trackmod_modplay_t *modplay, size_t chan,
310 uint8_t param)
311{
312 modplay->chan[chan].volume = param % (vol_max + 1);
313}
314
315/** Process Pattern break effect.
316 *
317 * @param modplay Module playback
318 * @param chan Channel number
319 * @param param Effect parameter
320 */
321static void trackmod_effect_pattern_break(trackmod_modplay_t *modplay,
322 size_t chan, uint8_t param)
323{
324 size_t next_idx;
325 trackmod_pattern_t *next_pat;
326 unsigned row;
327
328 /* Strangely the parameter is BCD */
329 row = (param >> 4) * 10 + (param & 0xf);
330
331 next_idx = trackmod_get_next_ord_idx(modplay);
332 next_pat = &modplay->module->pattern[next_idx];
333
334 modplay->pat_break = true;
335 modplay->pat_break_row = row % next_pat->rows;
336}
337
338/** Process Set speed effect.
339 *
340 * @param modplay Module playback
341 * @param chan Channel number
342 * @param param Effect parameter
343 */
344static void trackmod_effect_set_speed(trackmod_modplay_t *modplay, size_t chan,
345 uint8_t param)
346{
347 if (param > 0 && param < 32)
348 modplay->tpr = param;
349 else if (param > 0)
350 modplay->bpm = param;
351}
352
353/** Process Fine volume slide down effect.
354 *
355 * @param modplay Module playback
356 * @param chan Channel number
357 * @param param Effect parameter
358 */
359static void trackmod_effect_fine_vol_slide_down(trackmod_modplay_t *modplay,
360 size_t chan, uint8_t param)
361{
362 int nv;
363
364 nv = modplay->chan[chan].volume - param;
365 if (nv < 0)
366 nv = 0;
367 modplay->chan[chan].volume = nv;
368}
369
370/** Process Fine volume slide up effect.
371 *
372 * @param modplay Module playback
373 * @param chan Channel number
374 * @param param Effect parameter
375 */
376static void trackmod_effect_fine_vol_slide_up(trackmod_modplay_t *modplay,
377 size_t chan, uint8_t param)
378{
379 int nv;
380
381 nv = modplay->chan[chan].volume + param;
382 if (nv > vol_max)
383 nv = vol_max;
384 modplay->chan[chan].volume = nv;
385}
386
387/** Process Volume slide effect.
388 *
389 * @param modplay Module playback
390 * @param chan Channel number
391 * @param param Effect parameter
392 */
393static void trackmod_effect_vol_slide(trackmod_modplay_t *modplay,
394 size_t chan, uint8_t param)
395{
396 if ((param & 0xf0) != 0)
397 modplay->chan[chan].vol_slide = param >> 4;
398 else
399 modplay->chan[chan].vol_slide = -(int)(param & 0xf);
400}
401
402/** Process Volume slide down effect.
403 *
404 * @param modplay Module playback
405 * @param chan Channel number
406 * @param param Effect parameter
407 */
408static void trackmod_effect_vol_slide_down(trackmod_modplay_t *modplay,
409 size_t chan, uint8_t param4)
410{
411 modplay->chan[chan].vol_slide = -(int)param4;
412}
413
414/** Process Volume slide up effect.
415 *
416 * @param modplay Module playback
417 * @param chan Channel number
418 * @param param Effect parameter
419 */
420static void trackmod_effect_vol_slide_up(trackmod_modplay_t *modplay,
421 size_t chan, uint8_t param4)
422{
423 modplay->chan[chan].vol_slide = param4;
424}
425
426/** Process Fine portamento down effect.
427 *
428 * @param modplay Module playback
429 * @param chan Channel number
430 * @param param Effect parameter
431 */
432static void trackmod_effect_fine_porta_down(trackmod_modplay_t *modplay,
433 size_t chan, uint8_t param)
434{
435 int np;
436
437 np = modplay->chan[chan].period + param;
438 if (np > period_max)
439 np = period_max;
440 modplay->chan[chan].period = np;
441}
442
443/** Process Fine portamento up effect.
444 *
445 * @param modplay Module playback
446 * @param chan Channel number
447 * @param param Effect parameter
448 */
449static void trackmod_effect_fine_porta_up(trackmod_modplay_t *modplay,
450 size_t chan, uint8_t param)
451{
452 int np;
453
454 np = modplay->chan[chan].period - param;
455 if (np < period_min)
456 np = period_min;
457 modplay->chan[chan].period = np;
458}
459
460/** Process Portamento down effect.
461 *
462 * @param modplay Module playback
463 * @param chan Channel number
464 * @param param Effect parameter
465 */
466static void trackmod_effect_porta_down(trackmod_modplay_t *modplay,
467 size_t chan, uint8_t param)
468{
469 modplay->chan[chan].portamento = -(int)param;
470}
471
472/** Process Portamento up effect.
473 *
474 * @param modplay Module playback
475 * @param chan Channel number
476 * @param param Effect parameter
477 */
478static void trackmod_effect_porta_up(trackmod_modplay_t *modplay,
479 size_t chan, uint8_t param)
480{
481 modplay->chan[chan].portamento = param;
482}
483
484/** Process Tone portamento effect.
485 *
486 * @param modplay Module playback
487 * @param chan Channel number
488 * @param param Effect parameter
489 */
490static void trackmod_effect_tone_porta(trackmod_modplay_t *modplay,
491 size_t chan, uint8_t param)
492{
493 /* Set up tone portamento effect */
494 modplay->chan[chan].portamento = param;
495 if (modplay->chan[chan].period_new != 0)
496 modplay->chan[chan].period_tgt = modplay->chan[chan].period_new;
497
498 /* Prevent going directly to new period */
499 modplay->chan[chan].period_new = 0;
500}
501
502/** Process volume column.
503 *
504 * @param modplay Module playback
505 * @param chan Channel number
506 * @param cell Cell
507 */
508static void trackmod_process_volume(trackmod_modplay_t *modplay, size_t chan,
509 trackmod_cell_t *cell)
510{
511 uint8_t param4;
512
513 if (cell->volume >= 0x10 && cell->volume <= 0x10 + vol_max)
514 trackmod_effect_set_volume(modplay, chan, cell->volume - 0x10);
515
516 param4 = cell->volume & 0xf;
517
518 switch (cell->volume & 0xf0) {
519 case 0x60:
520 trackmod_effect_vol_slide_down(modplay, chan, param4);
521 break;
522 case 0x70:
523 trackmod_effect_vol_slide_up(modplay, chan, param4);
524 break;
525 case 0x80:
526 trackmod_effect_fine_vol_slide_down(modplay, chan, param4);
527 break;
528 case 0x90:
529 trackmod_effect_fine_vol_slide_up(modplay, chan, param4);
530 break;
531 case 0xf0:
532 trackmod_effect_tone_porta(modplay, chan, param4 << 4);
533 break;
534 default:
535 break;
536 }
537}
538
539/** Process effect.
540 *
541 * @param modplay Module playback
542 * @param chan Channel number
543 * @param cell Cell
544 */
545static void trackmod_process_effect(trackmod_modplay_t *modplay, size_t chan,
546 trackmod_cell_t *cell)
547{
548 uint8_t param8;
549 uint8_t param4;
550
551 param8 = cell->effect & 0xff;
552
553 switch (cell->effect & 0xf00) {
554 case 0x100:
555 trackmod_effect_porta_up(modplay, chan, param8);
556 break;
557 case 0x200:
558 trackmod_effect_porta_down(modplay, chan, param8);
559 break;
560 case 0x300:
561 trackmod_effect_tone_porta(modplay, chan, param8);
562 break;
563 case 0xa00:
564 trackmod_effect_vol_slide(modplay, chan, param8);
565 break;
566 case 0xc00:
567 trackmod_effect_set_volume(modplay, chan, param8);
568 break;
569 case 0xd00:
570 trackmod_effect_pattern_break(modplay, chan, param8);
571 break;
572 case 0xf00:
573 trackmod_effect_set_speed(modplay, chan, param8);
574 break;
575 default:
576 break;
577 }
578
579 param4 = cell->effect & 0xf;
580
581 switch (cell->effect & 0xff0) {
582 case 0xe10:
583 trackmod_effect_fine_porta_up(modplay, chan, param4);
584 break;
585 case 0xe20:
586 trackmod_effect_fine_porta_down(modplay, chan, param4);
587 break;
588 case 0xea0:
589 trackmod_effect_fine_vol_slide_up(modplay, chan, param4);
590 break;
591 case 0xeb0:
592 trackmod_effect_fine_vol_slide_down(modplay, chan, param4);
593 break;
594 }
595}
596
597/** Process pattern cell.
598 *
599 * @param modplay Module playback
600 * @param chan Channel number
601 * @param cell Cell
602 */
603static void trackmod_process_cell(trackmod_modplay_t *modplay, size_t chan,
604 trackmod_cell_t *cell)
605{
606 modplay->chan[chan].period_new = 0;
607
608 trackmod_process_instr(modplay, chan, cell);
609
610 if (cell->period != 0 || (cell->note != 0 && cell->note != keyoff_note)) {
611 trackmod_process_note(modplay, chan, cell);
612 } else if (cell->note == keyoff_note && cell->instr == 0) {
613 trackmod_process_keyoff_note(modplay, chan);
614 }
615
616 trackmod_process_volume(modplay, chan, cell);
617 trackmod_process_effect(modplay, chan, cell);
618
619 if (modplay->chan[chan].period_new != 0)
620 modplay->chan[chan].period = modplay->chan[chan].period_new;
621}
622
623/** Process pattern row.
624 *
625 * @param modplay Module playback
626 */
627static void trackmod_process_row(trackmod_modplay_t *modplay)
628{
629 trackmod_pattern_t *pattern;
630 trackmod_cell_t cell;
631 size_t i;
632
633 pattern = trackmod_cur_pattern(modplay);
634
635 if (modplay->debug)
636 printf("%02zx: ", modplay->row);
637
638 for (i = 0; i < modplay->module->channels; i++) {
639 trackmod_pattern_get_cell(pattern, modplay->row, i, &cell);
640
641 if (modplay->debug) {
642 printf("%4d %02x %02x %03x |", cell.period ?
643 cell.period : cell.note, cell.instr,
644 cell.volume, cell.effect);
645 }
646
647 trackmod_process_cell(modplay, i, &cell);
648 }
649
650 if (modplay->debug)
651 printf("\n");
652}
653
654/** Get next order list index.
655 *
656 * @param modplay Module playback
657 * @return Next order list index
658 */
659static size_t trackmod_get_next_ord_idx(trackmod_modplay_t *modplay)
660{
661 size_t ord_idx;
662
663 ord_idx = modplay->ord_idx + 1;
664 if (ord_idx >= modplay->module->ord_list_len)
665 ord_idx = modplay->module->restart_pos;
666
667 return ord_idx;
668}
669
670/** Advance to next pattern.
671 *
672 * @param modplay Module playback
673 */
674static void trackmod_next_pattern(trackmod_modplay_t *modplay)
675{
676 if (modplay->debug)
677 printf("Next pattern\n");
678
679 modplay->row = 0;
680 modplay->ord_idx = trackmod_get_next_ord_idx(modplay);
681
682 /* If we are doing a pattern break */
683 if (modplay->pat_break) {
684 modplay->row = modplay->pat_break_row;
685 modplay->pat_break = false;
686 }
687}
688
689/** Clear effects at end of row. */
690static void trackmod_clear_effects(trackmod_modplay_t *modplay)
691{
692 size_t i;
693
694 for (i = 0; i < modplay->module->channels; i++) {
695 modplay->chan[i].vol_slide = 0;
696 modplay->chan[i].portamento = 0;
697 }
698}
699
700/** Process effects at beginning of tick. */
701static void trackmod_process_tick(trackmod_modplay_t *modplay)
702{
703 trackmod_chan_t *chan;
704 size_t i;
705 int nv;
706 int np;
707
708 for (i = 0; i < modplay->module->channels; i++) {
709 chan = &modplay->chan[i];
710
711 /* Volume slides */
712 nv = (int)chan->volume + chan->vol_slide;
713 if (nv < 0)
714 nv = 0;
715 if (nv > vol_max)
716 nv = vol_max;
717
718 chan->volume = nv;
719
720 /* Portamentos */
721 if (chan->period_tgt == 0) {
722 /* Up or down portamento */
723 np = (int)chan->period - chan->portamento;
724 } else {
725 /* Tone portamento */
726 if (chan->period_tgt < chan->period)
727 np = max((int)chan->period_tgt, (int)chan->period - chan->portamento);
728 else
729 np = min((int)chan->period_tgt, (int)chan->period + chan->portamento);
730 }
731
732#if 0
733 /* XXX */
734 if (np < period_min)
735 np = period_min;
736 if (np > period_max)
737 np = period_max;
738#endif
739 modplay->chan[i].period = np;
740 }
741}
742
743/** Advance to next row.
744 *
745 * @param modplay Module playback
746 */
747static void trackmod_next_row(trackmod_modplay_t *modplay)
748{
749 trackmod_pattern_t *pattern;
750
751 /* Clear effect state at end of row */
752 trackmod_clear_effects(modplay);
753
754 pattern = trackmod_cur_pattern(modplay);
755
756 modplay->tick = 0;
757 ++modplay->row;
758 if (modplay->row >= pattern->rows || modplay->pat_break)
759 trackmod_next_pattern(modplay);
760
761 trackmod_process_tick(modplay);
762 trackmod_process_row(modplay);
763}
764
765/** Advance to next tick.
766 *
767 * @param modplay Module playback
768 */
769static void trackmod_next_tick(trackmod_modplay_t *modplay)
770{
771 modplay->smp = 0;
772 ++modplay->tick;
773 if (modplay->tick >= modplay->tpr)
774 trackmod_next_row(modplay);
775 else
776 trackmod_process_tick(modplay);
777}
778
779/** Create module playback object.
780 *
781 * @param module Module
782 * @param smp_freq Sampling frequency
783 * @param rmodplay Place to store pointer to module playback object
784 */
785errno_t trackmod_modplay_create(trackmod_module_t *module,
786 unsigned smp_freq, trackmod_modplay_t **rmodplay)
787{
788 trackmod_modplay_t *modplay = NULL;
789
790 modplay = calloc(1, sizeof(trackmod_modplay_t));
791 if (modplay == NULL)
792 goto error;
793
794 modplay->module = module;
795 modplay->smp_freq = smp_freq;
796 modplay->frame_size = sizeof(int16_t);
797 modplay->ord_idx = 0;
798 modplay->row = 0;
799 modplay->tick = 0;
800 modplay->smp = 0;
801
802 modplay->tpr = module->def_tpr;
803 modplay->bpm = module->def_bpm;
804
805 modplay->chan = calloc(module->channels,
806 sizeof(trackmod_chan_t));
807 if (modplay->chan == NULL)
808 goto error;
809
810 trackmod_process_tick(modplay);
811 trackmod_process_row(modplay);
812
813 *rmodplay = modplay;
814 return EOK;
815error:
816 if (modplay != NULL)
817 free(modplay->chan);
818 free(modplay);
819 return ENOMEM;
820}
821
822/** Destroy module playback object.
823 *
824 * @param modplay Module playback
825 */
826void trackmod_modplay_destroy(trackmod_modplay_t *modplay)
827{
828 free(modplay->chan);
829 free(modplay);
830}
831
832/** Get number of samples per tick.
833 *
834 * @param modplay Module playback
835 * @return Number of samples per tick
836 */
837static size_t samples_per_tick(trackmod_modplay_t *modplay)
838{
839 return modplay->smp_freq * 10 / 4 / modplay->bpm;
840}
841
842/** Get number of samples remaining in current tick.
843 *
844 * @param modplay Module playback
845 * @return Number of remaining samples in tick
846 */
847static size_t samples_remain_tick(trackmod_modplay_t *modplay)
848{
849 /* XXX Integer samples per tick is a simplification */
850 return samples_per_tick(modplay) - modplay->smp;
851}
852
853/** Get sample frame.
854 *
855 * Get frame at the specified sample position.
856 *
857 * @param sample Sample
858 * @param pos Position (frame index)
859 * @return Frame value
860 */
861int trackmod_sample_get_frame(trackmod_sample_t *sample, size_t pos)
862{
863 int8_t *i8p;
864 int16_t *i16p;
865
866 if (sample->bytes_smp == 1) {
867 i8p = (int8_t *)sample->data;
868 return i8p[pos];
869 } else {
870 /* chan->sample->bytes_smp == 2 */
871 i16p = (int16_t *)sample->data;
872 return i16p[pos] / 256; /* XXX Retain full precision */
873 }
874}
875
876/** Advance sample position to next frame.
877 *
878 * @param chan Channel playback
879 */
880static void chan_smp_next_frame(trackmod_chan_t *chan)
881{
882 chan->lsmp = trackmod_sample_get_frame(chan->sample, chan->smp_pos);
883 ++chan->smp_pos;
884
885 switch (chan->sample->loop_type) {
886 case tl_pingpong_loop:
887 /** XXX Pingpong loop */
888 case tl_no_loop:
889 /* No loop */
890 if (chan->smp_pos >= chan->sample->length) {
891 chan->sample = NULL;
892 chan->smp_pos = 0;
893 }
894 break;
895 case tl_forward_loop:
896 /** Forward loop */
897 if (chan->smp_pos >= chan->sample->loop_start +
898 chan->sample->loop_len) {
899 chan->smp_pos = chan->sample->loop_start;
900 }
901 }
902}
903
904/** Render next sample on channel.
905 *
906 * @param modplay Module playback
907 * @param cidx Channel number
908 * @return Sample value
909 */
910static int trackmod_chan_next_sample(trackmod_modplay_t *modplay,
911 size_t cidx)
912{
913 int sl, sn;
914 int period, clk;
915 int s;
916
917 trackmod_chan_t *chan = &modplay->chan[cidx];
918
919 if (chan->sample == NULL || chan->period == 0)
920 return 0;
921
922 /*
923 * Linear interpolation. Note this is slightly simplified:
924 * We ignore the half-sample offset and the boundary condition
925 * at the end of the sample (we should extend with zero).
926 */
927 sl = (int)chan->lsmp * amp_factor * chan->volume / vol_max;
928 sn = (int)trackmod_sample_get_frame(chan->sample, chan->smp_pos) *
929 amp_factor * chan->volume / vol_max;
930
931 period = (int)chan->period;
932 clk = (int)chan->smp_clk;
933
934 s = (sl * (period - clk) + sn * clk) / period;
935
936 chan->smp_clk += base_clock / modplay->smp_freq;
937 while (chan->sample != NULL && chan->smp_clk >= chan->period) {
938 chan->smp_clk -= chan->period;
939 chan_smp_next_frame(chan);
940 }
941
942 return s;
943}
944
945/** Render a segment of samples contained entirely within a tick.
946 *
947 * @param modplay Module playback
948 * @param buffer Buffer for storing audio data
949 * @param bufsize Size of @a buffer in bytes
950 */
951static void get_samples_within_tick(trackmod_modplay_t *modplay,
952 void *buffer, size_t bufsize)
953{
954 size_t nsamples;
955 size_t smpidx;
956 size_t chan;
957 int s;
958
959 nsamples = bufsize / modplay->frame_size;
960
961 for (smpidx = 0; smpidx < nsamples; smpidx++) {
962 s = 0;
963 for (chan = 0; chan < modplay->module->channels; chan++) {
964 s += trackmod_chan_next_sample(modplay, chan);
965 }
966
967 ((int16_t *)buffer)[smpidx] = s;
968 }
969
970 modplay->smp += nsamples;
971}
972
973/** Render a segment of samples.
974 *
975 * @param modplay Module playback
976 * @param buffer Buffer for storing audio data
977 * @param bufsize Size of @a buffer in bytes
978 */
979void trackmod_modplay_get_samples(trackmod_modplay_t *modplay,
980 void *buffer, size_t bufsize)
981{
982 size_t nsamples;
983 size_t rsmp;
984 size_t now;
985
986 nsamples = bufsize / modplay->frame_size;
987 while (nsamples > 0) {
988 rsmp = samples_remain_tick(modplay);
989 if (rsmp == 0)
990 trackmod_next_tick(modplay);
991
992 rsmp = samples_remain_tick(modplay);
993 now = min(rsmp, nsamples);
994
995 get_samples_within_tick(modplay, buffer,
996 now * modplay->frame_size);
997 nsamples -= now;
998 buffer += now * modplay->frame_size;
999 }
1000}
1001
1002/** @}
1003 */
Note: See TracBrowser for help on using the repository browser.