source: mainline/uspace/lib/trackmod/trackmod.c@ b412168

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b412168 was 43dd72b7, checked in by Jiri Svoboda <jiri@…>, 11 years ago

Trackmod update: XM file format, new effects, etc.

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