📄 mod.c
字号:
/* TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp> Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi> Most of this file is taken from the MikMod sound library, which is (c) 1998, 1999 Miodrag Vallat and others - see file libunimod/AUTHORS for complete list. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*//* Interface to libunimod + module player */#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#include <stdio.h>#include <stdlib.h>#include <math.h>#ifdef SUNOSextern long int random (void);#endif#ifndef NO_STRING_H#include <string.h>#else#include <strings.h>#endif#include "timidity.h"#include "common.h"#include "instrum.h"#include "playmidi.h"#include "readmidi.h"#include "tables.h"#include "mod.h"#include "output.h"#include "controls.h"#include "unimod.h"#include "unimod_priv.h"#include "mod2midi.h"static BOOL mod_do_play (MODULE *);int load_module_file (struct timidity_file *tf, int mod_type){ MODULE *mf;#ifdef LOOKUP_HACK ML_8bitsamples = 1;#else ML_8bitsamples = 0;#endif ML_monosamples = 1; ML_RegisterAllLoaders (); mf = ML_Load (tf->url, MOD_NUM_VOICES, 0); if (ML_errno) return 1; current_file_info->file_type = mod_type; load_module_samples (mf->samples, mf->numsmp, mod_type == IS_MOD_FILE); mod_do_play (mf); ML_Free (mf); return 0;}int get_module_type (char *fn){ if (check_file_extension (fn, ".mod", 1)) /* Most common first */ return IS_MOD_FILE; if (check_file_extension (fn, ".xm", 1) || check_file_extension (fn, ".s3m", 1) || check_file_extension (fn, ".it", 1) || check_file_extension (fn, ".669", 1) /* Then the others in alphabetic order */ || check_file_extension (fn, ".amf", 1) || check_file_extension (fn, ".dsm", 1) || check_file_extension (fn, ".far", 1) || check_file_extension (fn, ".gdm", 1) || check_file_extension (fn, ".imf", 1) || check_file_extension (fn, ".med", 1) || check_file_extension (fn, ".mtm", 1) || check_file_extension (fn, ".stm", 1) || check_file_extension (fn, ".stx", 1) || check_file_extension (fn, ".ult", 1) || check_file_extension (fn, ".uni", 1)) return IS_S3M_FILE; return IS_OTHER_FILE;}char *get_module_title (struct timidity_file *tf, int mod_type){ return ML_LoadTitle (tf->url);}/*========== Playing */#define POS_NONE (-2) /* no loop position defined */typedef struct ENVPR{ UBYTE flg; /* envelope flag */ UBYTE pts; /* number of envelope points */ UBYTE susbeg; /* envelope sustain index begin */ UBYTE susend; /* envelope sustain index end */ UBYTE beg; /* envelope loop begin */ UBYTE end; /* envelope loop end */ SWORD p; /* current envelope counter */ UWORD a; /* envelope index a */ UWORD b; /* envelope index b */ ENVPT *env; /* envelope points */}ENVPR;typedef struct MP_CONTROL { INSTRUMENT *i; SAMPLE *s; UBYTE sample; /* which sample number */ UBYTE note; /* the audible note as heard, direct rep of period */ SWORD outvolume; /* output volume (vol + sampcol + instvol) */ SBYTE chanvol; /* channel's "global" volume */ UWORD fadevol; /* fading volume rate */ SWORD panning; /* panning position */ UBYTE kick; /* if true = sample has to be restarted */ UWORD period; /* period to play the sample at */ UBYTE nna; /* New note action type + master/slave flags */ UBYTE volflg; /* volume envelope settings */ UBYTE panflg; /* panning envelope settings */ UBYTE pitflg; /* pitch envelope settings */ UBYTE keyoff; /* if true = fade out and stuff */ SWORD *data; /* which sample-data to play */ UBYTE notedelay; /* (used for note delay) */ SLONG start; /* The starting byte index in the sample */ UWORD ultoffset; /* fine sample offset memory */ struct MP_VOICE *slave; /* Audio Slave of current effects control channel */ UBYTE slavechn; /* Audio Slave of current effects control channel */ UBYTE anote; /* the note that indexes the audible */ UBYTE oldnote; SWORD ownper; SWORD ownvol; UBYTE dca; /* duplicate check action */ UBYTE dct; /* duplicate check type */ UBYTE *row; /* row currently playing on this channel */ SBYTE retrig; /* retrig value (0 means don't retrig) */ ULONG speed; /* what finetune to use */ SWORD volume; /* amiga volume (0 t/m 64) to play the sample at */ SWORD tmpvolume; /* tmp volume */ UWORD tmpperiod; /* tmp period */ UWORD wantedperiod; /* period to slide to (with effect 3 or 5) */ UBYTE arpmem; /* arpeggio command memory */ UBYTE pansspd; /* panslide speed */ UWORD slidespeed; /* */ UWORD portspeed; /* noteslide speed (toneportamento) */ UBYTE s3mtremor; /* s3m tremor (effect I) counter */ UBYTE s3mtronof; /* s3m tremor ontime/offtime */ UBYTE s3mvolslide; /* last used volslide */ SBYTE sliding; UBYTE s3mrtgspeed; /* last used retrig speed */ UBYTE s3mrtgslide; /* last used retrig slide */ UBYTE glissando; /* glissando (0 means off) */ UBYTE wavecontrol; SBYTE vibpos; /* current vibrato position */ UBYTE vibspd; /* "" speed */ UBYTE vibdepth; /* "" depth */ SBYTE trmpos; /* current tremolo position */ UBYTE trmspd; /* "" speed */ UBYTE trmdepth; /* "" depth */ UBYTE fslideupspd; UBYTE fslidednspd; UBYTE fportupspd; /* fx E1 (extra fine portamento up) data */ UBYTE fportdnspd; /* fx E2 (extra fine portamento dn) data */ UBYTE ffportupspd; /* fx X1 (extra fine portamento up) data */ UBYTE ffportdnspd; /* fx X2 (extra fine portamento dn) data */ ULONG hioffset; /* last used high order of sample offset */ UWORD soffset; /* last used low order of sample-offset (effect 9) */ UBYTE sseffect; /* last used Sxx effect */ UBYTE ssdata; /* last used Sxx data info */ UBYTE chanvolslide; /* last used channel volume slide */ UBYTE panbwave; /* current panbrello waveform */ UBYTE panbpos; /* current panbrello position */ SBYTE panbspd; /* "" speed */ UBYTE panbdepth; /* "" depth */ UWORD newsamp; /* set to 1 upon a sample / inst change */ UBYTE voleffect; /* Volume Column Effect Memory as used by IT */ UBYTE voldata; /* Volume Column Data Memory */ SWORD pat_reppos; /* patternloop position */ UWORD pat_repcnt; /* times to loop */ }MP_CONTROL;/* Used by NNA only player (audio control. MP_CONTROL is used for full effects control). */typedef struct MP_VOICE { INSTRUMENT *i; SAMPLE *s; UBYTE sample; /* which instrument number */ SWORD volume; /* output volume (vol + sampcol + instvol) */ SWORD panning; /* panning position */ SBYTE chanvol; /* channel's "global" volume */ UWORD fadevol; /* fading volume rate */ UWORD period; /* period to play the sample at */ UBYTE volflg; /* volume envelope settings */ UBYTE panflg; /* panning envelope settings */ UBYTE pitflg; /* pitch envelope settings */ UBYTE keyoff; /* if true = fade out and stuff */ UBYTE kick; /* if true = sample has to be restarted */ UBYTE note; /* the audible note (as heard, direct rep of period) */ UBYTE nna; /* New note action type + master/slave flags */ SWORD *data; /* which sample-data to play */ SLONG start; /* The start byte index in the sample *//* Below here is info NOT in MP_CONTROL!! */ ENVPR venv; ENVPR penv; ENVPR cenv; UWORD avibpos; /* autovibrato pos */ UWORD aswppos; /* autovibrato sweep pos */ ULONG totalvol; /* total volume of channel (before global mixings) */ BOOL mflag; SWORD masterchn; UWORD masterperiod; MP_CONTROL *master; /* index of "master" effects channel */ }MP_VOICE;typedef struct MP_STATUS { SWORD channel; /* channel we're working on */ UWORD oldsngspd; /* old song speed */ UWORD sngspd; /* current song speed */ SWORD volume; /* song volume (0-128) (or user volume) */ UWORD bpm; /* current beats-per-minute speed */ UWORD newbpm; /* next beats-per-minute speed */ UBYTE realchn; /* real number of channels used */ UBYTE totalchn; /* total number of channels used (incl NNAs) */ UWORD patpos; /* current row number */ SWORD sngpos; /* current song position */ UWORD numrow; /* number of rows on current pattern */ UWORD vbtick; /* tick counter (counts from 0 to sngspd) */ struct MP_CONTROL *control; /* Effects Channel info (size pf->numchn) */ struct MP_VOICE voice[MOD_NUM_VOICES]; /* Audio Voice information */ UBYTE globalslide; /* global volume slide rate */ UBYTE pat_repcrazy; /* module has just looped to position -1 */ UWORD patbrk; /* position where to start a new pattern */ UBYTE patdly; /* patterndelay counter (command memory) */ UBYTE patdly2; /* patterndelay counter (real one) */ SWORD posjmp; /* flag to indicate a jump is needed... */ int explicitslides; }MP_STATUS;MODULE *pf = NULL; /* modfile being played */static MP_CONTROL *a; /* current AUDTMP we're working on */static MP_STATUS mp; /* player status */static UBYTE VibratoTable[32] ={ 0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24};static UBYTE avibtab[128] ={ 0, 1, 3, 4, 6, 7, 9, 10, 12, 14, 15, 17, 18, 20, 21, 23, 24, 25, 27, 28, 30, 31, 32, 34, 35, 36, 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 54, 55, 56, 57, 57, 58, 59, 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 64, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, 59, 59, 58, 57, 57, 56, 55, 54, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 42, 41, 40, 39, 38, 36, 35, 34, 32, 31, 30, 28, 27, 25, 24, 23, 21, 20, 18, 17, 15, 14, 12, 10, 9, 7, 6, 4, 3, 1};static SBYTE PanbrelloTable[256] ={ 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, 0, -2, -3, -5, -6, -8, -9, -11, -12, -14, -16, -17, -19, -20, -22, -23, -24, -26, -27, -29, -30, -32, -33, -34, -36, -37, -38, -39, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -56, -57, -58, -59, -59, -60, -60, -61, -61, -62, -62, -62, -63, -63, -63, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -63, -63, -63, -62, -62, -62, -61, -61, -60, -60, -59, -59, -58, -57, -56, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -39, -38, -37, -36, -34, -33, -32, -30, -29, -27, -26, -24, -23, -22, -20, -19, -17, -16, -14, -12, -11, -9, -8, -6, -5, -3, -2};/* returns a random value between 0 and ceil-1, ceil must be a power of two */static int getrandom (int ceil){#ifdef HAVE_SRANDOM return random () & (ceil - 1);#else return (rand () * ceil) / (RAND_MAX + 1.0);#endif}/* New Note Action Scoring System : -------------------------------- 1) total-volume (fadevol, chanvol, volume) is the main scorer. 2) a looping sample is a bonus x2 3) a foreground channel is a bonus x4 4) an active envelope with keyoff is a handicap -x2 */static int MP_FindEmptyChannel (void){ MP_VOICE *a; ULONG t, k, tvol, pp; a = mp.voice; for (t = 0; t < MOD_NUM_VOICES; t++) { /* allow us to take over a nonexisting sample */ if (!a->s) return t; if (((mp.voice[t].kick == KICK_ABSENT) || (mp.voice[t].kick == KICK_ENV)) && Voice_Stopped (t)) return t; } tvol = 0xffffffUL; t = 0; for (k = 0; k < MOD_NUM_VOICES; k++, a++) if ((a->kick == KICK_ABSENT) || (a->kick == KICK_ENV)) { pp = a->totalvol << ((a->s->flags & SF_LOOP) ? 1 : 0); if ((a->master) && (a == a->master->slave)) pp <<= 2; if (pp < tvol) { tvol = pp; t = k; } } if (tvol > 8000 * 7) return -1; return t;}static UWORD GetPeriod (UWORD note, ULONG speed){ if (pf->flags & UF_XMPERIODS) return (pf->flags & UF_LINEAR) ? getlinearperiod (note, speed) : getlogperiod (note, speed); return getoldperiod (note, speed);}static SWORD Interpolate (SWORD p, SWORD p1, SWORD p2, SWORD v1, SWORD v2){ if ((p1 == p2) || (p == p1)) return v1; return v1 + ((SLONG) ((p - p1) * (v2 - v1)) / (p2 - p1));}static SWORD InterpolateEnv (SWORD p, ENVPT * a, ENVPT * b){ return (Interpolate (p, a->pos, b->pos, a->val, b->val));}static SWORD DoPan (SWORD envpan, SWORD pan){ int newpan; newpan = pan + (((envpan - PAN_CENTER) * (128 - abs (pan - PAN_CENTER))) / 128); return (newpan < PAN_LEFT) ? PAN_LEFT : (newpan > PAN_RIGHT ? PAN_RIGHT : newpan);}static void StartEnvelope (ENVPR * t, UBYTE flg, UBYTE pts, UBYTE susbeg, UBYTE susend, UBYTE beg, UBYTE end, ENVPT * p, UBYTE keyoff){ t->flg = flg; t->pts = pts; t->susbeg = susbeg; t->susend = susend; t->beg = beg; t->end = end; t->env = p; t->p = 0; t->a = 0; t->b = ((t->flg & EF_SUSTAIN) && (!(keyoff & KEY_OFF))) ? 0 : 1; /* Imago Orpheus sometimes stores an extra initial point in the envelope */ if ((t->pts >= 2) && (t->env[0].pos == t->env[1].pos)) { t->a++; t->b++; } if (t->b >= t->pts) t->b = t->pts - 1;}/* This procedure processes all envelope types, include volume, pitch, and panning. Envelopes are defined by a set of points, each with a magnitude [relating either to volume, panning position, or pitch modifier] and a tick position. Envelopes work in the following manner: (a) Each tick the envelope is moved a point further in its progression. For an accurate progression, magnitudes between two envelope points are interpolated. (b) When progression reaches a defined point on the envelope, values are shifted to interpolate between this point and the next, and checks for loops or envelope end are done. Misc: Sustain loops are loops that are only active as long as the keyoff flag is clear. When a volume envelope terminates, so does the current fadeout. */static SWORD ProcessEnvelope (ENVPR * t, SWORD v, UBYTE keyoff){ if (t->flg & EF_ON) { UBYTE a, b; /* actual points in the envelope */ UWORD p; /* the 'tick counter' - real point being played */ a = t->a; b = t->b; p = t->p; /* if sustain loop on one point (XM type), don't move and don't interpolate when the point is reached */ if ((t->flg & EF_SUSTAIN) && (t->susbeg == t->susend) && (!(keyoff & KEY_OFF)) && (p == t->env[t->susbeg].pos)) v = t->env[t->susbeg].val; else { /* compute the current envelope value between points a and b */ if (a == b) v = t->env[a].val; else v = InterpolateEnv (p, &t->env[a], &t->env[b]); p++; /* pointer reached point b? */ if (p >= t->env[b].pos) { a = b++; /* shift points a and b */ /* Check for loops, sustain loops, or end of envelope. */ if ((t->flg & EF_SUSTAIN) && (!(keyoff & KEY_OFF)) && (b > t->susend)) { a = t->susbeg; b = (t->susbeg == t->susend) ? a : a + 1; p = t->env[a].pos; } else if ((t->flg & EF_LOOP) && (b > t->end)) { a = t->beg; b = (t->beg == t->end) ? a : a + 1; p = t->env[a].pos; } else { if (b >= t->pts) { if ((t->flg & EF_VOLENV) && (mp.channel != -1)) { mp.voice[mp.channel].keyoff |= KEY_FADE; if (!v) mp.voice[mp.channel].fadevol = 0; } b--; p--; } } } t->a = a; t->b = b; t->p = p; } } return v;}/*========== Protracker effects */static void DoEEffects (UBYTE dat){ UBYTE nib = dat & 0xf; switch (dat >> 4) { case 0x0: /* hardware filter toggle, not supported */ break; case 0x1: /* fineslide up */ if (a->period) if (!mp.vbtick) a->tmpperiod -= (nib << 2); break; case 0x2: /* fineslide dn */ if (a->period) if (!mp.vbtick) a->tmpperiod += (nib << 2); break; case 0x3: /* glissando ctrl */ a->glissando = nib; break; case 0x4: /* set vibrato waveform */ a->wavecontrol &= 0xf0; a->wavecontrol |= nib; break; case 0x5: /* set finetune */ if (a->period) { if (pf->flags & UF_XMPERIODS) a->speed = nib + 128; else a->speed = finetune[nib]; a->tmpperiod = GetPeriod ((UWORD) a->note << 1, a->speed); } break; case 0x6: /* set patternloop */ if (mp.vbtick) break; if (nib)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -