1590 lines
33 KiB
C
1590 lines
33 KiB
C
/*
|
|
* gmod.c - Module player for GUS and Linux.
|
|
* (C) Hannu Savolainen, 1993
|
|
*
|
|
* NOTE! This program doesn't try to be a complete module player.
|
|
* It's just a too I used while developing the driver. In
|
|
* addition it can be used as an example on programming
|
|
* the LInux Sound Driver with GUS.
|
|
* $Id$
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <machine/ultrasound.h>
|
|
#include <fcntl.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#define CMD_ARPEG 0x00
|
|
#define CMD_SLIDEUP 0x01
|
|
#define CMD_SLIDEDOWN 0x02
|
|
#define CMD_SLIDETO 0x03
|
|
#define SLIDE_SIZE 8
|
|
#define CMD_VOLSLIDE 0x0a
|
|
#define CMD_JUMP 0x0b
|
|
#define CMD_VOLUME 0x0c
|
|
#define CMD_BREAK 0x0d
|
|
#define CMD_SPEED 0x0f
|
|
#define CMD_NOP 0xfe
|
|
#define CMD_NONOTE 0xff
|
|
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
#define MAX_TRACK 8
|
|
#define MAX_PATTERN 128
|
|
#define MAX_POSITION 128
|
|
|
|
struct note_info
|
|
{
|
|
unsigned char note;
|
|
unsigned char vol;
|
|
unsigned char sample;
|
|
unsigned char command;
|
|
short parm1, parm2;
|
|
};
|
|
|
|
struct voice_info
|
|
{
|
|
int sample;
|
|
int note;
|
|
int volume;
|
|
int pitchbender;
|
|
|
|
/* Pitch sliding */
|
|
|
|
int slide_pitch;
|
|
int slide_goal;
|
|
int slide_rate;
|
|
|
|
int volslide;
|
|
};
|
|
|
|
typedef struct note_info pattern[MAX_TRACK][64];
|
|
int pattern_len[MAX_POSITION];
|
|
int pattern_tempo[MAX_POSITION];
|
|
pattern *pattern_table[MAX_PATTERN];
|
|
|
|
struct voice_info voices[MAX_TRACK];
|
|
|
|
int nr_channels, nr_patterns, songlength;
|
|
int tune[MAX_POSITION];
|
|
double tick_duration;
|
|
|
|
int period_table[] =
|
|
{
|
|
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
|
|
428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
|
|
214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113
|
|
};
|
|
|
|
SEQ_DEFINEBUF (2048);
|
|
|
|
int seqfd;
|
|
int sample_ok[128], sample_vol[128];
|
|
int tmp, gus_dev;
|
|
double this_time, next_time;
|
|
int ticks_per_division;
|
|
double clock_rate; /* HZ */
|
|
|
|
/*
|
|
* The function seqbuf_dump() must always be provided
|
|
*/
|
|
|
|
void play_module (char *name);
|
|
int load_module (char *name);
|
|
int play_note (int channel, struct note_info *pat);
|
|
void lets_play_voice (int channel, struct voice_info *v);
|
|
|
|
void
|
|
seqbuf_dump ()
|
|
{
|
|
if (_seqbufptr)
|
|
if (write (seqfd, _seqbuf, _seqbufptr) == -1)
|
|
{
|
|
perror ("write /dev/sequencer");
|
|
exit (-1);
|
|
}
|
|
_seqbufptr = 0;
|
|
}
|
|
|
|
void
|
|
init_voices ()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_TRACK; i++)
|
|
{
|
|
voices[i].sample = 0;
|
|
voices[i].note = 0;
|
|
voices[i].volume = 64;
|
|
|
|
voices[i].slide_pitch = 0;
|
|
voices[i].slide_goal = 0;
|
|
voices[i].slide_rate = 0;
|
|
voices[i].pitchbender = 0;
|
|
|
|
voices[i].volslide = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
int i, n, j;
|
|
struct synth_info info;
|
|
|
|
if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
|
|
{
|
|
perror ("/dev/sequencer");
|
|
exit (-1);
|
|
}
|
|
|
|
if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
|
|
{
|
|
perror ("/dev/sequencer");
|
|
exit (-1);
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
info.device = i;
|
|
|
|
if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
|
|
{
|
|
perror ("/dev/sequencer");
|
|
exit (-1);
|
|
}
|
|
|
|
if (info.synth_type == SYNTH_TYPE_SAMPLE
|
|
&& info.synth_subtype == SAMPLE_TYPE_GUS)
|
|
gus_dev = i;
|
|
}
|
|
|
|
if (gus_dev == -1)
|
|
{
|
|
fprintf (stderr, "Gravis Ultrasound not detected\n");
|
|
exit (-1);
|
|
}
|
|
|
|
GUS_NUMVOICES (gus_dev, 14);
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
for (j = 0; j < MAX_PATTERN; j++)
|
|
pattern_table[j] = NULL;
|
|
|
|
if (load_module (argv[i]))
|
|
{
|
|
tick_duration = 100.0 / clock_rate;
|
|
play_module (argv[i]);
|
|
}
|
|
|
|
}
|
|
|
|
SEQ_DUMPBUF ();
|
|
close (seqfd);
|
|
|
|
exit (0);
|
|
}
|
|
|
|
unsigned short
|
|
intelize (unsigned short v)
|
|
{
|
|
return ((v & 0xff) << 8) | ((v >> 8) & 0xff);
|
|
}
|
|
|
|
unsigned long
|
|
intelize4 (unsigned long v)
|
|
{
|
|
return
|
|
(((v >> 16) & 0xff) << 8) | (((v >> 16) >> 8) & 0xff) |
|
|
(((v & 0xff) << 8) | ((v >> 8) & 0xff) << 16);
|
|
}
|
|
|
|
int
|
|
load_stm_module (int mod_fd, char *name)
|
|
{
|
|
|
|
struct sample_header
|
|
{
|
|
char name[12];
|
|
unsigned char instr_disk;
|
|
unsigned short reserved1;
|
|
unsigned short length; /* In bytes */
|
|
unsigned short loop_start;
|
|
unsigned short loop_end;
|
|
unsigned char volume;
|
|
unsigned char reserved2;
|
|
unsigned short C2_speed;
|
|
unsigned short reserved3;
|
|
|
|
};
|
|
|
|
int i, total_mem;
|
|
int sample_ptr;
|
|
|
|
int position;
|
|
|
|
unsigned char *tune_ptr; /* array 0-127 */
|
|
|
|
char header[1105], sname[21];
|
|
|
|
int nr_samples; /* 16 or 32 samples (or 64 or ???) */
|
|
int slen, npat;
|
|
|
|
fprintf (stderr, "Loading .STM module: %s\n", name);
|
|
|
|
if (read (mod_fd, header, sizeof (header)) != sizeof (header))
|
|
{
|
|
fprintf (stderr, "%s: Short file (header)\n", name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
strncpy (sname, header, 20);
|
|
|
|
fprintf (stderr, "\nModule: %s - ", sname);
|
|
|
|
if (header[28] != 0x1a)
|
|
{
|
|
fprintf (stderr, "Not a STM module\n");
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
npat = header[33];
|
|
slen = 0;
|
|
tune_ptr = &header[48 + (31 * 32)];
|
|
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
tune[i] = tune_ptr[i];
|
|
if (tune[i] < npat)
|
|
slen = i;
|
|
}
|
|
|
|
fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
|
|
|
|
nr_samples = 31;
|
|
|
|
sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024); /* Location where the
|
|
* first sample is
|
|
* stored */
|
|
total_mem = 0;
|
|
|
|
for (i = 0; i < 32; i++)
|
|
sample_ok[i] = 0;
|
|
|
|
for (i = 0; i < nr_samples; i++)
|
|
{
|
|
int len, loop_start, loop_end, base_freq;
|
|
unsigned short loop_flags = 0;
|
|
|
|
struct sample_header *sample;
|
|
|
|
struct patch_info *patch;
|
|
|
|
sample = (struct sample_header *) &header[48 + (i * 32)];
|
|
|
|
len = sample->length;
|
|
loop_start = sample->loop_start;
|
|
loop_end = sample->loop_end;
|
|
base_freq = sample->C2_speed;
|
|
|
|
if (strlen (sample->name) > 21)
|
|
{
|
|
fprintf (stderr, "\nInvalid name for sample #%d\n", i);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if (len > 0)
|
|
{
|
|
int x;
|
|
|
|
if (loop_end > len)
|
|
loop_end = 1;
|
|
else if (loop_end < loop_start)
|
|
{
|
|
loop_start = 0;
|
|
loop_end = 0;
|
|
}
|
|
else
|
|
loop_flags = WAVE_LOOPING;
|
|
|
|
total_mem += len;
|
|
patch = (struct patch_info *) malloc (sizeof (*patch) + len);
|
|
|
|
patch->key = GUS_PATCH;
|
|
patch->device_no = gus_dev;
|
|
patch->instr_no = i;
|
|
patch->mode = loop_flags;
|
|
patch->len = len;
|
|
patch->loop_start = loop_start;
|
|
patch->loop_end = loop_end;
|
|
patch->base_freq = base_freq;
|
|
patch->base_note = 261630; /* Mid C */
|
|
patch->low_note = 0;
|
|
patch->high_note = 0x7fffffff;
|
|
patch->volume = 120;
|
|
|
|
if (lseek (mod_fd, sample_ptr, 0) == -1)
|
|
{
|
|
perror (name);
|
|
close (mod_fd);
|
|
free (patch);
|
|
return 0;
|
|
}
|
|
|
|
sample_ptr += len;
|
|
|
|
if ((x = read (mod_fd, patch->data, len)) != len)
|
|
{
|
|
fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len);
|
|
close (mod_fd);
|
|
free (patch);
|
|
return 0;
|
|
}
|
|
|
|
fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n",
|
|
i,
|
|
len,
|
|
loop_start,
|
|
loop_end,
|
|
base_freq,
|
|
sample->name);
|
|
|
|
if (write (seqfd, patch, sizeof (*patch) + len) == -1)
|
|
{
|
|
perror ("ioctl /dev/sequencer");
|
|
exit (-1);
|
|
}
|
|
else
|
|
sample_ok[i] = 1;
|
|
|
|
free (patch);
|
|
}
|
|
}
|
|
|
|
nr_patterns = slen;
|
|
songlength = slen;
|
|
nr_channels = 4;
|
|
|
|
for (position = 0; position < npat; position++)
|
|
{
|
|
unsigned char patterns[64][4][4];
|
|
int pat, channel, x;
|
|
|
|
int pp = 1104 + (position * 1024);
|
|
|
|
if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
|
|
{
|
|
fprintf (stderr, "Can't allocate memory for a pattern\n");
|
|
return 0;
|
|
}
|
|
|
|
if (lseek (mod_fd, pp, 0) == -1)
|
|
{
|
|
perror (name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if ((x = read (mod_fd, patterns, 1024)) != 1024)
|
|
{
|
|
fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
for (pat = 0; pat < 64; pat++)
|
|
{
|
|
|
|
for (channel = 0; channel < 4; channel++)
|
|
{
|
|
unsigned char *p;
|
|
|
|
unsigned vol, note, octave, sample, effect, params;
|
|
|
|
p = &patterns[pat][channel][0];
|
|
|
|
if (p[0] < 251)
|
|
{
|
|
note = p[0] & 15;
|
|
octave = p[0] / 16;
|
|
|
|
note = 48 + octave * 12 + note;
|
|
|
|
sample = p[1] / 8;
|
|
vol = (p[1] & 7) + (p[2] / 2);
|
|
effect = p[2] & 0xF;
|
|
params = p[3];
|
|
}
|
|
else
|
|
{
|
|
note = 0;
|
|
octave = 0;
|
|
|
|
sample = 0;
|
|
vol = 0;
|
|
effect = CMD_NONOTE;
|
|
params = 0;
|
|
}
|
|
|
|
(*pattern_table[position])[channel][pat].note = note;
|
|
(*pattern_table[position])[channel][pat].sample = sample;
|
|
(*pattern_table[position])[channel][pat].command = effect;
|
|
(*pattern_table[position])[channel][pat].parm1 = params;
|
|
(*pattern_table[position])[channel][pat].parm2 = 0;
|
|
(*pattern_table[position])[channel][pat].vol = vol;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close (mod_fd);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
load_669_module (int mod_fd, char *name)
|
|
{
|
|
struct sample_header
|
|
{
|
|
char name[13];
|
|
unsigned long length; /* In bytes */
|
|
unsigned long loop_start;
|
|
unsigned long loop_end;
|
|
};
|
|
|
|
int i, total_mem;
|
|
int sample_ptr;
|
|
|
|
int position;
|
|
|
|
unsigned char *tune_ptr, *len_ptr, *tempo_ptr; /* array 0-127 */
|
|
|
|
char header[1084];
|
|
char msg[110];
|
|
|
|
int nr_samples; /* 16 or 32 samples */
|
|
int slen, npat;
|
|
|
|
clock_rate = 25.0;
|
|
|
|
fprintf (stderr, "Loading .669 module: %s\n", name);
|
|
|
|
if (read (mod_fd, header, sizeof (header)) != sizeof (header))
|
|
{
|
|
fprintf (stderr, "%s: Short file (header)\n", name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if (*(unsigned short *) &header[0] != 0x6669)
|
|
{
|
|
fprintf (stderr, "Not a 669 file\n");
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
strncpy (msg, &header[2], 108);
|
|
|
|
for (i = 0; i < strlen (msg); i++)
|
|
if ((msg[i] >= ' ' && msg[i] <= 'z') || msg[i] == '\n')
|
|
printf ("%c", msg[i]);
|
|
printf ("\n");
|
|
|
|
npat = header[0x6f];
|
|
|
|
tune_ptr = &header[0x71];
|
|
|
|
for (slen = 0; slen < 128 && tune_ptr[slen] != 0xff; slen++);
|
|
slen--;
|
|
|
|
for (i = 0; i < slen; i++)
|
|
tune[i] = tune_ptr[i];
|
|
|
|
len_ptr = &header[0x171];
|
|
for (i = 0; i < slen; i++)
|
|
pattern_len[i] = len_ptr[i] - 1;
|
|
|
|
tempo_ptr = &header[0xf1];
|
|
for (i = 0; i < slen; i++)
|
|
pattern_tempo[i] = tempo_ptr[i];
|
|
|
|
nr_samples = header[0x6e];
|
|
|
|
fprintf (stderr, "Song lenght %d, %d patterns, %d samples.\n", slen, npat, nr_samples);
|
|
|
|
sample_ptr = 0x1f1 + (nr_samples * 0x19) + (npat * 0x600); /* Location where the
|
|
* first sample is
|
|
* stored */
|
|
total_mem = 0;
|
|
|
|
for (i = 0; i < 64; i++)
|
|
sample_ok[i] = 0;
|
|
|
|
for (i = 0; i < nr_samples; i++)
|
|
{
|
|
int len, loop_start, loop_end;
|
|
unsigned short loop_flags = 0;
|
|
|
|
struct sample_header *sample;
|
|
char sname[14];
|
|
|
|
struct patch_info *patch;
|
|
|
|
sample = (struct sample_header *) &header[0x1f1 + (i * 0x19)];
|
|
|
|
len = *(unsigned long *) &sample->name[13];
|
|
loop_start = *(unsigned long *) &sample->name[17];
|
|
loop_end = *(unsigned long *) &sample->name[21];
|
|
if (loop_end > len)
|
|
loop_end = 1;
|
|
else if (loop_end == len)
|
|
loop_end--;
|
|
|
|
if (loop_end < loop_start)
|
|
{
|
|
loop_start = 0;
|
|
loop_end = 0;
|
|
}
|
|
|
|
strncpy (sname, sample->name, 13);
|
|
|
|
if (len > 0 && len < 200000)
|
|
{
|
|
total_mem += len;
|
|
|
|
fprintf (stderr, "Sample %02d: %05d, %05d, %05d %s\n",
|
|
i,
|
|
len,
|
|
loop_start,
|
|
loop_end,
|
|
sname);
|
|
|
|
patch = (struct patch_info *) malloc (sizeof (*patch) + len);
|
|
|
|
if (loop_end == 0)
|
|
loop_end = 1;
|
|
if (loop_end >= len)
|
|
loop_end = 1;
|
|
|
|
if (loop_end > 1) loop_flags = WAVE_LOOPING;
|
|
|
|
patch->key = GUS_PATCH;
|
|
patch->device_no = gus_dev;
|
|
patch->instr_no = i;
|
|
patch->mode = WAVE_UNSIGNED | loop_flags;
|
|
patch->len = len;
|
|
patch->loop_start = loop_start;
|
|
patch->loop_end = loop_end;
|
|
patch->base_freq = 8448;
|
|
patch->base_note = 261630;
|
|
patch->low_note = 1000;
|
|
patch->high_note = 0x7fffffff;
|
|
patch->volume = 120;
|
|
|
|
if (lseek (mod_fd, sample_ptr, 0) == -1)
|
|
{
|
|
fprintf (stderr, "Seek failed\n");
|
|
perror (name);
|
|
close (mod_fd);
|
|
free (patch);
|
|
return 0;
|
|
}
|
|
|
|
sample_ptr += len;
|
|
|
|
if (read (mod_fd, patch->data, len) != len)
|
|
{
|
|
fprintf (stderr, "Short file (sample at %d)\n", sample_ptr);
|
|
close (mod_fd);
|
|
free (patch);
|
|
return 0;
|
|
}
|
|
|
|
if (write (seqfd, patch, sizeof (*patch) + len) == -1)
|
|
{
|
|
perror ("ioctl /dev/sequencer");
|
|
/* exit (-1); */
|
|
}
|
|
else
|
|
sample_ok[i] = 1;
|
|
|
|
free (patch);
|
|
}
|
|
}
|
|
|
|
nr_patterns = slen;
|
|
songlength = slen;
|
|
nr_channels = 8;
|
|
|
|
for (position = 0; position < npat; position++)
|
|
{
|
|
unsigned char patterns[0x600];
|
|
int pat, channel, x;
|
|
|
|
int pp = 0x1f1 + (nr_samples * 0x19) + (position * 0x600);
|
|
|
|
if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
|
|
{
|
|
fprintf (stderr, "Can't allocate memory for a pattern\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
if (lseek (mod_fd, pp, 0) == -1)
|
|
{
|
|
perror (name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if ((x = read (mod_fd, patterns, 1024)) != 1024)
|
|
{
|
|
fprintf (stderr, "Short file (pattern at %d) %d!=1024\n", pp, x);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
for (pat = 0; pat < 64; pat++)
|
|
{
|
|
|
|
for (channel = 0; channel < 8; channel++)
|
|
{
|
|
unsigned char *p;
|
|
|
|
unsigned vol, period, sample, effect, params;
|
|
|
|
p = &patterns[pat * 24 + channel * 3];
|
|
|
|
if (p[0] >= 0xfe ||
|
|
(p[0] == 0xff && p[1] == 0xff && p[2] == 0xff) ||
|
|
(p[0] == 0 && p[1] == 0 && p[2] == 0) ||
|
|
*(int *) p == -1)
|
|
{
|
|
period = 0;
|
|
effect = CMD_NONOTE;
|
|
sample = 0;
|
|
vol = 0;
|
|
params = 0;
|
|
|
|
if (p[0] == 0)
|
|
{
|
|
effect = CMD_BREAK;
|
|
params = -2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
period = (p[0] >> 2) + 48;
|
|
effect = (p[2] >> 4);
|
|
params = p[2] & 0x0f;
|
|
vol = p[1] & 0x0f;
|
|
|
|
if (p[2] == 0xfe)
|
|
{
|
|
effect = CMD_VOLUME;
|
|
params = vol;
|
|
}
|
|
else if (p[2] == 0xff)
|
|
{
|
|
effect = CMD_NOP;
|
|
}
|
|
else
|
|
switch (effect)
|
|
{
|
|
case 0: /* a - Portamento up */
|
|
effect = CMD_SLIDEUP;
|
|
break;
|
|
|
|
case 1: /* b - Portamento down */
|
|
effect = CMD_SLIDEDOWN;
|
|
break;
|
|
|
|
case 2: /* c - Port to note */
|
|
effect = CMD_SLIDETO;
|
|
break;
|
|
|
|
case 3: /* d - Frequency adjust */
|
|
effect = CMD_NOP; /* To be implemented */
|
|
break;
|
|
|
|
case 4: /* e - Frequency vibrato */
|
|
effect = CMD_NOP; /* To be implemented */
|
|
break;
|
|
|
|
case 5: /* f - Set tempo */
|
|
effect = CMD_SPEED;
|
|
break;
|
|
|
|
default:
|
|
effect = CMD_NOP;
|
|
}
|
|
|
|
sample = (((p[0] << 4) & 0x30) | ((p[1] >> 4) & 0x0f)) + 1;
|
|
}
|
|
|
|
(*pattern_table[position])[channel][pat].note = period;
|
|
(*pattern_table[position])[channel][pat].sample = sample;
|
|
(*pattern_table[position])[channel][pat].command = effect;
|
|
(*pattern_table[position])[channel][pat].parm1 = params;
|
|
(*pattern_table[position])[channel][pat].parm2 = 0;
|
|
(*pattern_table[position])[channel][pat].vol = vol;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close (mod_fd);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
load_mmd0_module (int mod_fd, char *name)
|
|
{
|
|
|
|
struct sample_header
|
|
{
|
|
unsigned short loop_start;
|
|
unsigned short loop_end;
|
|
unsigned char midich;
|
|
unsigned char midipreset;
|
|
unsigned char volume;
|
|
unsigned char strans;
|
|
};
|
|
|
|
int i, total_mem;
|
|
int sample_ptr;
|
|
|
|
int position;
|
|
|
|
unsigned char *tune_ptr; /* array 0-127 */
|
|
|
|
char header[1105];
|
|
|
|
int nr_samples; /* 16 or 32 samples (or 64 or ???) */
|
|
int slen, npat;
|
|
|
|
fprintf (stderr, "Loading .MED module: %s\n", name);
|
|
|
|
if (read (mod_fd, header, sizeof (header)) != sizeof (header))
|
|
{
|
|
fprintf (stderr, "%s: Short file (header)\n", name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if (strncmp (header, "MMD0", 4))
|
|
{
|
|
fprintf (stderr, "Not a MED module\n");
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
printf ("Module len %d\n", intelize4 (*(long *) &header[4]));
|
|
printf ("Song info %d\n", intelize4 (*(long *) &header[8]));
|
|
printf ("Song len %d\n", intelize4 (*(long *) &header[12]));
|
|
printf ("Blockarr %x\n", intelize4 (*(long *) &header[16]));
|
|
printf ("Blockarr len %d\n", intelize4 (*(long *) &header[20]));
|
|
printf ("Sample array %x\n", intelize4 (*(long *) &header[24]));
|
|
printf ("Sample array len %d\n", intelize4 (*(long *) &header[28]));
|
|
printf ("Exp data %x\n", intelize4 (*(long *) &header[32]));
|
|
printf ("Exp size %d\n", intelize4 (*(long *) &header[36]));
|
|
printf ("Pstate %d\n", intelize (*(long *) &header[40]));
|
|
printf ("Pblock %d\n", intelize (*(long *) &header[42]));
|
|
|
|
return 0;
|
|
|
|
npat = header[33];
|
|
slen = 0;
|
|
tune_ptr = &header[48 + (31 * 32)];
|
|
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
tune[i] = tune_ptr[i];
|
|
if (tune[i] < npat)
|
|
slen = i;
|
|
}
|
|
|
|
fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
|
|
|
|
nr_samples = 31;
|
|
|
|
sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024); /* Location where the
|
|
* first sample is
|
|
* stored */
|
|
total_mem = 0;
|
|
|
|
for (i = 0; i < 32; i++)
|
|
sample_ok[i] = 0;
|
|
|
|
for (i = 0; i < nr_samples; i++)
|
|
{
|
|
int len, loop_start, loop_end, base_freq;
|
|
unsigned short loop_flags = 0;
|
|
|
|
struct sample_header *sample;
|
|
|
|
struct patch_info *patch;
|
|
|
|
sample = (struct sample_header *) &header[48 + (i * 32)];
|
|
|
|
/*
|
|
* len = sample->length; loop_start = sample->loop_start; loop_end =
|
|
* sample->loop_end; base_freq = sample->C2_speed;
|
|
*
|
|
* if (strlen (sample->name) > 21) { fprintf (stderr, "\nInvalid name for
|
|
* sample #%d\n", i); close (mod_fd); return 0; }
|
|
*/
|
|
if (len > 0)
|
|
{
|
|
int x;
|
|
|
|
if (loop_end > len)
|
|
loop_end = 1;
|
|
|
|
if (loop_end < loop_start)
|
|
{
|
|
loop_start = 0;
|
|
loop_end = 0;
|
|
}
|
|
|
|
if (loop_end > 2) loop_flags = WAVE_LOOPING;
|
|
|
|
total_mem += len;
|
|
patch = (struct patch_info *) malloc (sizeof (*patch) + len);
|
|
|
|
patch->key = GUS_PATCH;
|
|
patch->device_no = gus_dev;
|
|
patch->instr_no = i;
|
|
patch->mode = loop_flags;
|
|
patch->len = len;
|
|
patch->loop_start = loop_start;
|
|
patch->loop_end = loop_end;
|
|
patch->base_freq = base_freq;
|
|
patch->base_note = 261630; /* Mid C */
|
|
patch->low_note = 0;
|
|
patch->high_note = 0x7fffffff;
|
|
patch->volume = 120;
|
|
|
|
if (lseek (mod_fd, sample_ptr, 0) == -1)
|
|
{
|
|
perror (name);
|
|
close (mod_fd);
|
|
free (patch);
|
|
return 0;
|
|
}
|
|
|
|
sample_ptr += len;
|
|
|
|
if ((x = read (mod_fd, patch->data, len)) != len)
|
|
{
|
|
fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len);
|
|
close (mod_fd);
|
|
free (patch);
|
|
return 0;
|
|
}
|
|
/*
|
|
* fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n", i,
|
|
* len, loop_start, loop_end, base_freq, sample->name);
|
|
*/
|
|
if (write (seqfd, patch, sizeof (*patch) + len) == -1)
|
|
{
|
|
perror ("ioctl /dev/sequencer");
|
|
exit (-1);
|
|
}
|
|
else
|
|
sample_ok[i] = 1;
|
|
|
|
free (patch);
|
|
}
|
|
}
|
|
|
|
nr_patterns = slen;
|
|
songlength = slen;
|
|
nr_channels = 4;
|
|
|
|
for (position = 0; position < npat; position++)
|
|
{
|
|
unsigned char patterns[64][4][4];
|
|
int pat, channel, x;
|
|
|
|
int pp = 1104 + (position * 1024);
|
|
|
|
if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
|
|
{
|
|
fprintf (stderr, "Can't allocate memory for a pattern\n");
|
|
return 0;
|
|
}
|
|
|
|
if (lseek (mod_fd, pp, 0) == -1)
|
|
{
|
|
perror (name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if ((x = read (mod_fd, patterns, 1024)) != 1024)
|
|
{
|
|
fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
for (pat = 0; pat < 64; pat++)
|
|
{
|
|
|
|
for (channel = 0; channel < 4; channel++)
|
|
{
|
|
unsigned char *p;
|
|
|
|
unsigned vol, note, octave, sample, effect, params;
|
|
|
|
p = &patterns[pat][channel][0];
|
|
|
|
if (p[0] < 251)
|
|
{
|
|
note = p[0] & 15;
|
|
octave = p[0] / 16;
|
|
|
|
note = 48 + octave * 12 + note;
|
|
|
|
sample = p[1] / 8;
|
|
vol = (p[1] & 7) + (p[2] / 2);
|
|
effect = p[2] & 0xF;
|
|
params = p[3];
|
|
}
|
|
else
|
|
{
|
|
note = 0;
|
|
octave = 0;
|
|
|
|
sample = 0;
|
|
vol = 0;
|
|
effect = CMD_NONOTE;
|
|
params = 0;
|
|
}
|
|
|
|
(*pattern_table[position])[channel][pat].note = note;
|
|
(*pattern_table[position])[channel][pat].sample = sample;
|
|
(*pattern_table[position])[channel][pat].command = effect;
|
|
(*pattern_table[position])[channel][pat].parm1 = params;
|
|
(*pattern_table[position])[channel][pat].parm2 = 0;
|
|
(*pattern_table[position])[channel][pat].vol = vol;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close (mod_fd);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
load_module (char *name)
|
|
{
|
|
|
|
struct sample_header
|
|
{
|
|
char name[22];
|
|
unsigned short length; /* In words */
|
|
|
|
unsigned char finetune;
|
|
unsigned char volume;
|
|
|
|
unsigned short repeat_point; /* In words */
|
|
unsigned short repeat_length; /* In words */
|
|
};
|
|
|
|
int i, mod_fd, total_mem;
|
|
int sample_ptr, pattern_loc;
|
|
|
|
int position;
|
|
|
|
unsigned char *tune_ptr; /* array 0-127 */
|
|
|
|
char header[1084];
|
|
|
|
int nr_samples; /* 16 or 32 samples */
|
|
int slen, npat;
|
|
char mname[23];
|
|
|
|
ioctl (seqfd, SNDCTL_SEQ_SYNC, 0);
|
|
ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev);
|
|
|
|
clock_rate = 50.0;
|
|
|
|
for (i = 0; i < MAX_POSITION; i++)
|
|
pattern_len[i] = 64;
|
|
|
|
for (i = 0; i < MAX_POSITION; i++)
|
|
pattern_tempo[i] = 0;
|
|
|
|
if ((mod_fd = open (name, O_RDONLY, 0)) == -1)
|
|
{
|
|
perror (name);
|
|
return 0;
|
|
}
|
|
|
|
if (read (mod_fd, header, sizeof (header)) != sizeof (header))
|
|
{
|
|
fprintf (stderr, "%s: Short file (header)\n", name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if (lseek (mod_fd, 0, 0) == -1)
|
|
{
|
|
perror (name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if (header[28] == 0x1a)
|
|
return load_stm_module (mod_fd, name);
|
|
|
|
if (*(unsigned short *) &header[0] == 0x6669)
|
|
return load_669_module (mod_fd, name);
|
|
|
|
if (!strncmp (header, "MMD0", 4))
|
|
return load_mmd0_module (mod_fd, name);
|
|
|
|
fprintf (stderr, "Loading .MOD module: %s\n", name);
|
|
|
|
strncpy (mname, header, 22);
|
|
fprintf (stderr, "\nModule: %s - ", mname);
|
|
|
|
if (!strncmp (&header[1080], "M.K.", 4) || !strncmp (&header[1080], "FLT8", 4))
|
|
{
|
|
fprintf (stderr, "31 samples\n");
|
|
nr_samples = 31;
|
|
}
|
|
else
|
|
{
|
|
fprintf (stderr, "15 samples\n");
|
|
nr_samples = 15;
|
|
}
|
|
|
|
if (nr_samples == 31)
|
|
{
|
|
sample_ptr = pattern_loc = 1084;
|
|
slen = header[950];
|
|
tune_ptr = (unsigned char *) &header[952];
|
|
}
|
|
else
|
|
{
|
|
sample_ptr = pattern_loc = 600;
|
|
slen = header[470];
|
|
tune_ptr = (unsigned char *) &header[472];
|
|
}
|
|
|
|
npat = 0;
|
|
for (i = 0; i < 128; i++)
|
|
{
|
|
tune[i] = tune_ptr[i];
|
|
|
|
if (tune_ptr[i] > npat)
|
|
npat = tune_ptr[i];
|
|
}
|
|
npat++;
|
|
|
|
fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
|
|
|
|
sample_ptr += (npat * 1024); /* Location where the first sample is stored */
|
|
total_mem = 0;
|
|
|
|
for (i = 0; i < 32; i++)
|
|
sample_ok[i] = 0;
|
|
|
|
for (i = 0; i < nr_samples; i++)
|
|
{
|
|
int len, loop_start, loop_end;
|
|
unsigned short loop_flags = 0;
|
|
char pname[22];
|
|
|
|
struct sample_header *sample;
|
|
|
|
struct patch_info *patch;
|
|
|
|
sample = (struct sample_header *) &header[20 + (i * 30)];
|
|
|
|
len = intelize (sample->length) * 2;
|
|
loop_start = intelize (sample->repeat_point) * 2;
|
|
loop_end = loop_start + (intelize (sample->repeat_length) * 2);
|
|
|
|
if (loop_start > len)
|
|
loop_start = 0;
|
|
if (loop_end > len)
|
|
loop_end = len;
|
|
|
|
if (loop_end <= loop_start)
|
|
loop_end = loop_start + 1;
|
|
|
|
if (loop_end > 2 && loop_end > loop_start)
|
|
loop_flags = WAVE_LOOPING;
|
|
|
|
strncpy (pname, sample->name, 20);
|
|
|
|
if (len > 0)
|
|
{
|
|
fprintf (stderr, "Sample %02d: L%05d, S%05d, E%05d V%02d %s\n",
|
|
i,
|
|
len,
|
|
loop_start,
|
|
loop_end,
|
|
sample->volume,
|
|
pname);
|
|
|
|
total_mem += len;
|
|
|
|
patch = (struct patch_info *) malloc (sizeof (*patch) + len);
|
|
|
|
patch->key = GUS_PATCH;
|
|
patch->device_no = gus_dev;
|
|
patch->instr_no = i;
|
|
patch->mode = loop_flags;
|
|
patch->len = len;
|
|
patch->loop_start = loop_start;
|
|
patch->loop_end = loop_end;
|
|
patch->base_note = 261630; /* Middle C */
|
|
patch->base_freq = 8448;
|
|
patch->low_note = 0;
|
|
patch->high_note = 20000000;
|
|
patch->volume = 120;
|
|
patch->panning = 0;
|
|
|
|
if (lseek (mod_fd, sample_ptr, 0) == -1)
|
|
{
|
|
perror (name);
|
|
close (mod_fd);
|
|
free (patch);
|
|
return 0;
|
|
}
|
|
|
|
sample_ptr += len;
|
|
|
|
if (read (mod_fd, patch->data, len) != len)
|
|
{
|
|
fprintf (stderr, "Short file (sample) %d\n", sample_ptr);
|
|
close (mod_fd);
|
|
free (patch);
|
|
return 0;
|
|
}
|
|
|
|
SEQ_WRPATCH (patch, sizeof (*patch) + len);
|
|
|
|
sample_ok[i] = 1;
|
|
if (sample->volume == 0) sample->volume = 64;
|
|
sample_vol[i] = sample->volume;
|
|
|
|
free (patch);
|
|
}
|
|
}
|
|
|
|
nr_patterns = npat;
|
|
songlength = slen;
|
|
nr_channels = 4;
|
|
|
|
for (position = 0; position < npat; position++)
|
|
{
|
|
unsigned char patterns[64][4][4];
|
|
int pat, channel;
|
|
|
|
int pp = pattern_loc + (position * 1024);
|
|
|
|
if (lseek (mod_fd, pp, 0) == -1)
|
|
{
|
|
perror (name);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if (read (mod_fd, patterns, 1024) != 1024)
|
|
{
|
|
fprintf (stderr, "Short file (pattern %d) %d\n", tune[position], pp);
|
|
close (mod_fd);
|
|
return 0;
|
|
}
|
|
|
|
if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
|
|
{
|
|
fprintf (stderr, "Can't allocate memory for a pattern\n");
|
|
return 0;
|
|
}
|
|
|
|
for (pat = 0; pat < 64; pat++)
|
|
{
|
|
for (channel = 0; channel < 4; channel++)
|
|
{
|
|
unsigned short tmp;
|
|
unsigned char *p;
|
|
|
|
unsigned period, sample, effect, params, note, vol;
|
|
|
|
p = &patterns[pat][channel][0];
|
|
|
|
tmp = (p[0] << 8) | p[1];
|
|
sample = (tmp >> 8) & 0x10;
|
|
period =
|
|
MIN (tmp & 0xFFF, 1023);
|
|
tmp = (p[2] << 8) | p[3];
|
|
sample |= tmp >> 12;
|
|
effect = (tmp >> 8) & 0xF;
|
|
params = tmp & 0xFF;
|
|
|
|
note = 0;
|
|
|
|
if (period)
|
|
{
|
|
/*
|
|
* Convert period to a Midi note number
|
|
*/
|
|
|
|
for (note = 0; note < 37 && period != period_table[note]; note++);
|
|
if (note >= 37)
|
|
note = 0;
|
|
|
|
note += 48;
|
|
}
|
|
|
|
vol = 64;
|
|
|
|
if (sample)
|
|
if (effect == 0xc)
|
|
{
|
|
vol = params;
|
|
}
|
|
else
|
|
vol = sample_vol[sample - 1];
|
|
|
|
vol *= 2;
|
|
if (vol>64)vol--;
|
|
|
|
(*pattern_table[position])[channel][pat].note = note;
|
|
(*pattern_table[position])[channel][pat].sample = sample;
|
|
(*pattern_table[position])[channel][pat].command = effect;
|
|
(*pattern_table[position])[channel][pat].parm1 = params;
|
|
(*pattern_table[position])[channel][pat].parm2 = 0;
|
|
(*pattern_table[position])[channel][pat].vol = vol;
|
|
}
|
|
}
|
|
}
|
|
|
|
close (mod_fd);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
panning (int ch)
|
|
{
|
|
static int panning_tab[] =
|
|
{-110, 110, 110, -110};
|
|
|
|
return panning_tab[ch % 4];
|
|
}
|
|
|
|
void
|
|
set_speed (int parm)
|
|
{
|
|
if (!parm)
|
|
parm = 1;
|
|
|
|
if (parm < 32)
|
|
{
|
|
ticks_per_division = parm;
|
|
}
|
|
else
|
|
{
|
|
tick_duration = (60.0 / parm) * 10.0;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
play_module (char *name)
|
|
{
|
|
int i, position, jump_to_pos;
|
|
|
|
init_voices ();
|
|
|
|
SEQ_START_TIMER ();
|
|
#if 1
|
|
for (i=0;i<32;i++)
|
|
{
|
|
SEQ_EXPRESSION(gus_dev, i, 127);
|
|
SEQ_MAIN_VOLUME(gus_dev, i, 100);
|
|
}
|
|
#endif
|
|
next_time = 0.0;
|
|
|
|
set_speed (6);
|
|
|
|
for (position = 0; position < songlength; position++)
|
|
{
|
|
int tick, pattern, channel, pos, go_to;
|
|
|
|
pos = tune[position];
|
|
if (pattern_tempo[position])
|
|
set_speed (pattern_tempo[position]);
|
|
|
|
jump_to_pos = -1;
|
|
for (pattern = 0; pattern < pattern_len[position] && jump_to_pos == -1; pattern++)
|
|
{
|
|
this_time = 0.0;
|
|
|
|
for (channel = 0; channel < nr_channels; channel++)
|
|
{
|
|
if ((go_to = play_note (channel, &(*pattern_table[pos])[channel][pattern])) != -1)
|
|
jump_to_pos = go_to;
|
|
|
|
}
|
|
|
|
next_time += tick_duration;
|
|
|
|
for (tick = 1; tick < ticks_per_division; tick++)
|
|
{
|
|
for (channel = 0; channel < nr_channels; channel++)
|
|
lets_play_voice (channel, &voices[channel]);
|
|
next_time += tick_duration;
|
|
}
|
|
|
|
}
|
|
|
|
if (jump_to_pos >= 0)
|
|
position = jump_to_pos;
|
|
}
|
|
|
|
SEQ_WAIT_TIME ((int) next_time + 200); /* Wait extra 2 secs */
|
|
|
|
for (i = 0; i < nr_channels; i++)
|
|
SEQ_STOP_NOTE (gus_dev, i, 0, 127);
|
|
SEQ_DUMPBUF ();
|
|
|
|
for (i = 0; i < nr_patterns; i++)
|
|
free (pattern_table[i]);
|
|
}
|
|
|
|
void
|
|
sync_time ()
|
|
{
|
|
if (next_time > this_time)
|
|
{
|
|
SEQ_WAIT_TIME ((long) next_time);
|
|
this_time = next_time;
|
|
}
|
|
}
|
|
|
|
void
|
|
set_volslide (int channel, struct note_info *pat)
|
|
{
|
|
int n;
|
|
|
|
voices[channel].volslide = 0;
|
|
|
|
if ((n = (pat->parm1 & 0xf0) >> 4))
|
|
voices[channel].volslide = n;
|
|
else
|
|
voices[channel].volslide = pat->parm1 & 0xf;
|
|
}
|
|
|
|
void
|
|
set_slideto (int channel, struct note_info *pat)
|
|
{
|
|
int size, rate, dir, range = 200;
|
|
|
|
rate = pat->parm1;
|
|
size = voices[channel].note - pat->note;
|
|
if (!size)
|
|
return;
|
|
|
|
if (size < 0)
|
|
{
|
|
size *= -1;
|
|
dir = -1;
|
|
}
|
|
else
|
|
dir = 1;
|
|
|
|
if (size > 2)
|
|
{
|
|
range = size * 100;
|
|
rate = rate * size / 200;
|
|
}
|
|
|
|
rate = pat->parm1 * dir / 30;
|
|
if (!rate)
|
|
rate = 1;
|
|
|
|
voices[channel].slide_pitch = 1;
|
|
voices[channel].slide_goal = (dir * 8192 * 200 * 2 / size) / range;
|
|
voices[channel].pitchbender = 0;
|
|
voices[channel].slide_rate = rate;
|
|
SEQ_BENDER_RANGE (gus_dev, channel, range);
|
|
}
|
|
|
|
int
|
|
play_note (int channel, struct note_info *pat)
|
|
{
|
|
int jump = -1;
|
|
int sample;
|
|
|
|
if (pat->sample == 0x3f)
|
|
pat->sample = 0;
|
|
|
|
if (pat->command == CMD_NONOTE)
|
|
return -1; /* Undefined */
|
|
|
|
sample = pat->sample;
|
|
|
|
if (sample && !pat->note)
|
|
{
|
|
pat->note = voices[channel].note;
|
|
}
|
|
|
|
if (sample)
|
|
voices[channel].sample = sample;
|
|
else
|
|
sample = voices[channel].sample;
|
|
|
|
sample--;
|
|
|
|
if (pat->note && pat->command != 3) /* Have a note -> play */
|
|
{
|
|
if (sample < 0)
|
|
sample = voices[channel].sample - 1;
|
|
|
|
if (!sample_ok[sample])
|
|
sample = voices[channel].sample - 1;
|
|
|
|
if (sample < 0)
|
|
sample = 0;
|
|
|
|
if (sample_ok[sample])
|
|
{
|
|
sync_time ();
|
|
|
|
if (pat->vol > 127) pat->vol=127;
|
|
SEQ_SET_PATCH (gus_dev, channel, sample);
|
|
SEQ_PANNING (gus_dev, channel, panning (channel));
|
|
SEQ_PITCHBEND (gus_dev, channel, 0);
|
|
SEQ_START_NOTE (gus_dev, channel, pat->note, pat->vol);
|
|
|
|
voices[channel].volume = pat->vol;
|
|
voices[channel].note = pat->note;
|
|
voices[channel].slide_pitch = 0;
|
|
}
|
|
else
|
|
SEQ_STOP_NOTE (gus_dev, channel, pat->note, pat->vol);
|
|
}
|
|
|
|
switch (pat->command)
|
|
{
|
|
|
|
case CMD_NOP:;
|
|
break;
|
|
|
|
case CMD_JUMP:
|
|
jump = pat->parm1;
|
|
break;
|
|
|
|
case CMD_BREAK:
|
|
jump = -2;
|
|
break;
|
|
|
|
case CMD_SPEED:
|
|
set_speed (pat->parm1);
|
|
break;
|
|
|
|
case CMD_SLIDEUP:
|
|
voices[channel].slide_pitch = 1;
|
|
voices[channel].slide_goal = 8191;
|
|
voices[channel].pitchbender = 0;
|
|
voices[channel].slide_rate = pat->parm1 * SLIDE_SIZE;
|
|
SEQ_BENDER_RANGE (gus_dev, channel, 200);
|
|
break;
|
|
|
|
case CMD_SLIDEDOWN:
|
|
voices[channel].slide_pitch = 1;
|
|
voices[channel].slide_goal = -8192;
|
|
voices[channel].pitchbender = 0;
|
|
voices[channel].slide_rate = -pat->parm1 * SLIDE_SIZE;
|
|
SEQ_BENDER_RANGE (gus_dev, channel, 200);
|
|
break;
|
|
|
|
case CMD_SLIDETO:
|
|
set_slideto (channel, pat);
|
|
break;
|
|
|
|
case CMD_VOLUME:
|
|
{
|
|
int vol = pat->parm1*2;
|
|
if (vol>127) vol=127;
|
|
if (pat->note && pat->command != 3)
|
|
break;
|
|
SEQ_START_NOTE (gus_dev, channel, 255, vol);
|
|
}
|
|
break;
|
|
|
|
case CMD_ARPEG:
|
|
break;
|
|
|
|
case 0x0e:
|
|
/* printf ("Cmd 0xE%02x\n", pat->parm1); */
|
|
break;
|
|
|
|
case CMD_VOLSLIDE:
|
|
set_slideto (channel, pat);
|
|
break;
|
|
|
|
default:
|
|
/* printf ("Command %x %02x\n", pat->command, pat->parm1); */
|
|
}
|
|
|
|
return jump;
|
|
}
|
|
|
|
void
|
|
lets_play_voice (int channel, struct voice_info *v)
|
|
{
|
|
if (v->slide_pitch)
|
|
{
|
|
v->pitchbender += v->slide_rate;
|
|
if (v->slide_goal < 0)
|
|
{
|
|
if (v->pitchbender <= v->slide_goal)
|
|
{
|
|
v->pitchbender = v->slide_goal;
|
|
v->slide_pitch = 0; /* Stop */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (v->pitchbender >= v->slide_goal)
|
|
{
|
|
v->pitchbender = v->slide_goal;
|
|
v->slide_pitch = 0; /* Stop */
|
|
}
|
|
}
|
|
|
|
sync_time ();
|
|
SEQ_PITCHBEND (gus_dev, channel, v->pitchbender);
|
|
}
|
|
|
|
if (v->volslide)
|
|
{
|
|
v->volume += v->volslide;
|
|
sync_time ();
|
|
|
|
if (v->volume > 127) v->volume = 127;
|
|
SEQ_START_NOTE (gus_dev, channel, 255, v->volume);
|
|
}
|
|
}
|