Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/trackmod/trackmod.c

    r7e69e0e r43dd72b7  
    4040
    4141#include "macros.h"
     42#include "protracker.h"
    4243#include "trackmod.h"
     44#include "xm.h"
    4345
    4446/** Tunables */
     
    5254        base_clock = 8363 * 428,
    5355        /** Maximum sample volume */
    54         vol_max = 63,
    55         /** Default TPR */
    56         def_tpr = 6,
    57         /** Default BPM */
    58         def_bpm = 125
     56        vol_max = 64,
     57        /** Minimum period */
     58        period_min = 113,
     59        /** Maxium period */
     60        period_max = 856
    5961};
    6062
     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
    6187static size_t trackmod_get_next_ord_idx(trackmod_modplay_t *);
    6288
     
    7096}
    7197
     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
    72110/** Destroy pattern.
    73111 *
     
    97135
    98136        /* Destroy samples */
    99         if (module->sample != NULL) {
    100                 for (i = 0; i < module->samples; i++)
    101                         trackmod_sample_destroy(&module->sample[i]);
    102                 free(module->sample);
     137        if (module->instr != NULL) {
     138                for (i = 0; i < module->instrs; i++)
     139                        trackmod_instr_destroy(&module->instr[i]);
     140                free(module->instr);
    103141        }
    104142
     
    114152}
    115153
     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
    116167/** Return current pattern.
    117168 *
     
    137188    size_t row, size_t channel, trackmod_cell_t *cell)
    138189{
    139         uint32_t code;
    140 
    141         code = pattern->data[row * pattern->channels + channel];
    142         cell->period = (code >> (4 * 4)) & 0xfff;
    143         cell->sample = (((code >> (7 * 4)) & 0xf) << 4) |
    144             ((code >> (3 * 4)) & 0xf);
    145         cell->effect = code & 0xfff;
    146 }
    147 
    148 /** Process note (period, sample index)
     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)
    149220 *
    150221 * @param modplay Module playback
     
    156227{
    157228        trackmod_chan_t *chan = &modplay->chan[i];
    158         size_t smpidx;
    159 
    160         smpidx = (cell->sample - 1) % modplay->module->samples;
    161         chan->sample = &modplay->module->sample[smpidx];
     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];
    162280        chan->smp_pos = 0;
    163281        chan->lsmp = 0;
    164         chan->period = cell->period;
     282
    165283        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;
    166300}
    167301
     
    175309    uint8_t param)
    176310{
    177         modplay->chan[chan].volume = param & vol_max;
     311        modplay->chan[chan].volume = param % (vol_max + 1);
    178312}
    179313
     
    189323        size_t next_idx;
    190324        trackmod_pattern_t *next_pat;
     325        unsigned row;
     326
     327        /* Strangely the parameter is BCD */
     328        row = (param >> 4) * 10 + (param & 0xf);
    191329
    192330        next_idx = trackmod_get_next_ord_idx(modplay);
     
    194332
    195333        modplay->pat_break = true;
    196         modplay->pat_break_row = param % next_pat->rows;
     334        modplay->pat_break_row = row % next_pat->rows;
    197335}
    198336
     
    212350}
    213351
     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
    214538/** Process effect.
    215539 *
     
    222546{
    223547        uint8_t param8;
     548        uint8_t param4;
    224549
    225550        param8 = cell->effect & 0xff;
    226551
    227552        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;
    228565        case 0xc00:
    229566                trackmod_effect_set_volume(modplay, chan, param8);
     
    238575                break;
    239576        }
     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        }
    240594}
    241595
     
    249603    trackmod_cell_t *cell)
    250604{
    251         if (cell->period != 0 && cell->sample != 0)
     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)) {
    252610                trackmod_process_note(modplay, chan, cell);
    253 
     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);
    254616        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;
    255620}
    256621
     
    267632        pattern = trackmod_cur_pattern(modplay);
    268633
     634        if (modplay->debug)
     635                printf("%02zx: ", modplay->row);
     636
    269637        for (i = 0; i < modplay->module->channels; i++) {
    270638                trackmod_pattern_get_cell(pattern, modplay->row, i, &cell);
    271                 if (modplay->debug)
    272                         printf("%4d %02x %03x |", cell.period, cell.sample, cell.effect);
     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
    273646                trackmod_process_cell(modplay, i, &cell);
    274647        }
     
    289662        ord_idx = modplay->ord_idx + 1;
    290663        if (ord_idx >= modplay->module->ord_list_len)
    291                 ord_idx = 0; /* XXX */
     664                ord_idx = modplay->module->restart_pos;
    292665
    293666        return ord_idx;
     
    313686}
    314687
     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
    315740/** Advance to next row.
    316741 *
     
    320745{
    321746        trackmod_pattern_t *pattern;
     747
     748        /* Clear effect state at end of row */
     749        trackmod_clear_effects(modplay);
    322750
    323751        pattern = trackmod_cur_pattern(modplay);
     
    328756                trackmod_next_pattern(modplay);
    329757
     758        trackmod_process_tick(modplay);
    330759        trackmod_process_row(modplay);
    331760}
     
    341770        if (modplay->tick >= modplay->tpr)
    342771                trackmod_next_row(modplay);
     772        else
     773                trackmod_process_tick(modplay);
    343774}
    344775
     
    366797        modplay->smp = 0;
    367798
    368         modplay->tpr = def_tpr;
    369         modplay->bpm = def_bpm;
     799        modplay->tpr = module->def_tpr;
     800        modplay->bpm = module->def_bpm;
    370801
    371802        modplay->chan = calloc(module->channels,
     
    374805                goto error;
    375806
     807        trackmod_process_tick(modplay);
    376808        trackmod_process_row(modplay);
    377809
     
    416848}
    417849
     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
    418873/** Advance sample position to next frame.
    419874 *
     
    422877static void chan_smp_next_frame(trackmod_chan_t *chan)
    423878{
    424         chan->lsmp = chan->sample->data[chan->smp_pos];
     879        chan->lsmp = trackmod_sample_get_frame(chan->sample, chan->smp_pos);
    425880        ++chan->smp_pos;
    426881
    427         if (chan->sample->loop_len == 0) {
    428                 /* No looping */
     882        switch (chan->sample->loop_type) {
     883        case tl_pingpong_loop:
     884                /** XXX Pingpong loop */
     885        case tl_no_loop:
     886                /* No loop */
    429887                if (chan->smp_pos >= chan->sample->length) {
    430888                        chan->sample = NULL;
    431889                        chan->smp_pos = 0;
    432890                }
    433         } else {
    434                 /** Looping */
     891                break;
     892        case tl_forward_loop:
     893                /** Forward loop */
    435894                if (chan->smp_pos >= chan->sample->loop_start +
    436895                    chan->sample->loop_len) {
     
    455914        trackmod_chan_t *chan = &modplay->chan[cidx];
    456915
    457         if (chan->sample == NULL)
     916        if (chan->sample == NULL || chan->period == 0)
    458917                return 0;
    459918
     
    464923         */
    465924        sl = (int)chan->lsmp * amp_factor * chan->volume / vol_max;
    466         sn = (int)chan->sample->data[chan->smp_pos] * amp_factor *
    467             chan->volume / vol_max;
     925        sn = (int)trackmod_sample_get_frame(chan->sample, chan->smp_pos) *
     926            amp_factor * chan->volume / vol_max;
    468927
    469928        period = (int)chan->period;
Note: See TracChangeset for help on using the changeset viewer.