413 lines
9.7 KiB
C
413 lines
9.7 KiB
C
/************************************************************************
|
|
patchload.c -- loads patches for playmidi package
|
|
Some of this code was adapted from code written by Hannu Solovainen
|
|
|
|
Copyright (C) 1994-1996 Nathan I. Laredo
|
|
|
|
This program is modifiable/redistributable under the terms
|
|
of the GNU General Public Licence.
|
|
|
|
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
Send your comments and all your spare pocket change to
|
|
laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401
|
|
Kelly Drive, Lackland AFB, TX 78236-5128, USA.
|
|
*************************************************************************/
|
|
/* edited by Peter Kutak */
|
|
/* email : kutak@stonline.sk */
|
|
|
|
#if defined(linux) || defined(__FreeBSD__)
|
|
|
|
#include "playmidi.h"
|
|
#ifdef linux
|
|
#include <linux/ultrasound.h>
|
|
#else
|
|
#include <machine/ultrasound.h>
|
|
#endif
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include "gmvoices.h"
|
|
|
|
SEQ_USE_EXTBUF();
|
|
extern int play_gus, play_sb, play_ext, playing, verbose, force8bit;
|
|
extern int reverb, fmloaded[256], patchloaded[256];
|
|
extern int gus_dev, sb_dev, ext_dev, seqfd, wantopl3;
|
|
extern struct synth_info card_info[MAX_CARDS];
|
|
|
|
static int use8bit = 0;
|
|
|
|
struct pat_header {
|
|
char magic[12];
|
|
char version[10];
|
|
char description[60];
|
|
unsigned char instruments;
|
|
char voices;
|
|
char channels;
|
|
unsigned short nr_waveforms;
|
|
unsigned short master_volume;
|
|
unsigned int data_size;
|
|
};
|
|
|
|
struct sample_header {
|
|
char name[7];
|
|
unsigned char fractions;
|
|
int len;
|
|
int loop_start;
|
|
int loop_end;
|
|
unsigned short base_freq;
|
|
int low_note;
|
|
int high_note;
|
|
int base_note;
|
|
short detune;
|
|
unsigned char panning;
|
|
|
|
unsigned char envelope_rate[6];
|
|
unsigned char envelope_offset[6];
|
|
|
|
unsigned char tremolo_sweep;
|
|
unsigned char tremolo_rate;
|
|
unsigned char tremolo_depth;
|
|
|
|
unsigned char vibrato_sweep;
|
|
unsigned char vibrato_rate;
|
|
unsigned char vibrato_depth;
|
|
|
|
char modes;
|
|
|
|
short scale_frequency;
|
|
unsigned short scale_factor;
|
|
};
|
|
|
|
struct patch_info *patch;
|
|
int spaceleft, totalspace;
|
|
|
|
void gus_reload_8_bit();
|
|
|
|
void gus_load(pgm)
|
|
int pgm;
|
|
{
|
|
int i, j, patfd, offset;
|
|
struct pat_header header;
|
|
struct sample_header sample;
|
|
char buf[256], name[256];
|
|
struct stat info;
|
|
|
|
if (pgm < 0) {
|
|
use8bit = force8bit;
|
|
GUS_NUMVOICES(gus_dev, (card_info[gus_dev].nr_voices = 32));
|
|
SEQ_DUMPBUF();
|
|
for (i = 0; i < 256; i++)
|
|
patchloaded[i] = 0;
|
|
if (ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
|
|
{
|
|
/* error: should quit */
|
|
}
|
|
spaceleft = gus_dev;
|
|
ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &spaceleft);
|
|
totalspace = spaceleft;
|
|
}
|
|
|
|
if (patchloaded[pgm] < 0)
|
|
return;
|
|
|
|
if (patchloaded[pgm] == 1)
|
|
return;
|
|
|
|
if (gmvoice[pgm] == NULL) {
|
|
patchloaded[pgm] = -1;
|
|
return;
|
|
}
|
|
sprintf(name, PATCH_PATH1 "/%s.pat", gmvoice[pgm]);
|
|
|
|
if (stat(name, &info) == -1) {
|
|
sprintf(name, PATCH_PATH2 "/%s.pat", gmvoice[pgm]);
|
|
if (stat(name, &info) == -1)
|
|
return;
|
|
}
|
|
if ((patfd = open(name, O_RDONLY, 0)) == -1)
|
|
return;
|
|
if (spaceleft < info.st_size) {
|
|
if (!use8bit)
|
|
gus_reload_8_bit();
|
|
if (use8bit)
|
|
if (spaceleft < info.st_size / 2) {
|
|
close(patfd);
|
|
patchloaded[pgm] = -1; /* no space for patch */
|
|
return;
|
|
}
|
|
}
|
|
if (read(patfd, buf, 0xef) != 0xef) {
|
|
close(patfd);
|
|
return;
|
|
}
|
|
memcpy((char *) &header, buf, sizeof(header));
|
|
|
|
if (strncmp(header.magic, "GF1PATCH110", 12)) {
|
|
close(patfd);
|
|
return;
|
|
}
|
|
if (strncmp(header.version, "ID#000002", 10)) {
|
|
close(patfd);
|
|
return;
|
|
}
|
|
header.nr_waveforms = *(unsigned short *) &buf[85];
|
|
header.master_volume = *(unsigned short *) &buf[87];
|
|
|
|
offset = 0xef;
|
|
|
|
for (i = 0; i < header.nr_waveforms; i++) {
|
|
|
|
if (lseek(patfd, offset, 0) == -1) {
|
|
close(patfd);
|
|
return;
|
|
}
|
|
if (read(patfd, &buf, sizeof(sample)) != sizeof(sample)) {
|
|
close(patfd);
|
|
return;
|
|
}
|
|
memcpy((char *) &sample, buf, sizeof(sample));
|
|
|
|
/*
|
|
* Since some fields of the patch record are not 32bit aligned, we must
|
|
* handle them specially.
|
|
*/
|
|
sample.low_note = *(int *) &buf[22];
|
|
sample.high_note = *(int *) &buf[26];
|
|
sample.base_note = *(int *) &buf[30];
|
|
sample.detune = *(short *) &buf[34];
|
|
sample.panning = (unsigned char) buf[36];
|
|
|
|
memcpy(sample.envelope_rate, &buf[37], 6);
|
|
memcpy(sample.envelope_offset, &buf[43], 6);
|
|
|
|
sample.tremolo_sweep = (unsigned char) buf[49];
|
|
sample.tremolo_rate = (unsigned char) buf[50];
|
|
sample.tremolo_depth = (unsigned char) buf[51];
|
|
|
|
sample.vibrato_sweep = (unsigned char) buf[52];
|
|
sample.vibrato_rate = (unsigned char) buf[53];
|
|
sample.vibrato_depth = (unsigned char) buf[54];
|
|
sample.modes = (unsigned char) buf[55];
|
|
sample.scale_frequency = *(short *) &buf[56];
|
|
sample.scale_factor = *(unsigned short *) &buf[58];
|
|
|
|
offset = offset + 96;
|
|
patch = (struct patch_info *) malloc(sizeof(*patch) + sample.len);
|
|
|
|
if (patch == NULL) {
|
|
close(patfd);
|
|
return;
|
|
}
|
|
patch->key = GUS_PATCH;
|
|
patch->device_no = gus_dev;
|
|
patch->instr_no = pgm;
|
|
patch->mode = sample.modes | WAVE_TREMOLO |
|
|
WAVE_VIBRATO | WAVE_SCALE;
|
|
patch->len = (use8bit ? sample.len / 2 : sample.len);
|
|
patch->loop_start =
|
|
(use8bit ? sample.loop_start / 2 : sample.loop_start);
|
|
patch->loop_end = (use8bit ? sample.loop_end / 2 : sample.loop_end);
|
|
patch->base_note = sample.base_note;
|
|
patch->high_note = sample.high_note;
|
|
patch->low_note = sample.low_note;
|
|
patch->base_freq = sample.base_freq;
|
|
patch->detuning = sample.detune;
|
|
patch->panning = (sample.panning - 7) * 16;
|
|
|
|
memcpy(patch->env_rate, sample.envelope_rate, 6);
|
|
for (j = 0; j < 6; j++) /* tone things down slightly */
|
|
patch->env_offset[j] =
|
|
(736 * sample.envelope_offset[j] + 384) / 768;
|
|
|
|
if (reverb)
|
|
if (pgm < 120)
|
|
patch->env_rate[3] = (2 << 6) | (12 - (reverb >> 4));
|
|
else if (pgm > 127)
|
|
patch->env_rate[1] = (3 << 6) | (63 - (reverb >> 1));
|
|
|
|
patch->tremolo_sweep = sample.tremolo_sweep;
|
|
patch->tremolo_rate = sample.tremolo_rate;
|
|
patch->tremolo_depth = sample.tremolo_depth;
|
|
|
|
patch->vibrato_sweep = sample.vibrato_sweep;
|
|
patch->vibrato_rate = sample.vibrato_rate;
|
|
patch->vibrato_depth = sample.vibrato_depth;
|
|
|
|
patch->scale_frequency = sample.scale_frequency;
|
|
patch->scale_factor = sample.scale_factor;
|
|
|
|
patch->volume = header.master_volume;
|
|
|
|
if (lseek(patfd, offset, 0) == -1) {
|
|
close(patfd);
|
|
return;
|
|
}
|
|
if (read(patfd, patch->data, sample.len) != sample.len) {
|
|
close(patfd);
|
|
return;
|
|
}
|
|
if (patch->mode & WAVE_16_BITS && use8bit) {
|
|
patch->mode &= ~WAVE_16_BITS;
|
|
/* cut out every other byte to make 8-bit data from 16-bit */
|
|
for (j = 0; j < patch->len; j++)
|
|
patch->data[j] = patch->data[1 + j * 2];
|
|
}
|
|
SEQ_WRPATCH(patch, sizeof(*patch) + patch->len);
|
|
free(patch);
|
|
offset = offset + sample.len;
|
|
}
|
|
close(patfd);
|
|
spaceleft = gus_dev;
|
|
ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &spaceleft);
|
|
patchloaded[pgm] = 1;
|
|
return;
|
|
}
|
|
|
|
void gus_reload_8_bit()
|
|
{
|
|
int i;
|
|
|
|
if (ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
|
|
{
|
|
/* error: should quit */
|
|
}
|
|
spaceleft = gus_dev;
|
|
ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &spaceleft);
|
|
totalspace = spaceleft;
|
|
use8bit = 1;
|
|
for (i = 0; i < 256; i++)
|
|
if (patchloaded[i] > 0) {
|
|
patchloaded[i] = 0;
|
|
gus_load(i);
|
|
}
|
|
}
|
|
|
|
void adjustfm(buf, key)
|
|
char *buf;
|
|
int key;
|
|
{
|
|
unsigned char pan = ((rand() % 3) + 1) << 4;
|
|
|
|
if (key == FM_PATCH) {
|
|
buf[39] &= 0xc0;
|
|
if (buf[46] & 1)
|
|
buf[38] &= 0xc0;
|
|
buf[46] = (buf[46] & 0xcf) | pan;
|
|
if (reverb) {
|
|
unsigned val;
|
|
val = buf[43] & 0x0f;
|
|
if (val > 0)
|
|
val--;
|
|
buf[43] = (buf[43] & 0xf0) | val;
|
|
}
|
|
} else {
|
|
int mode;
|
|
if (buf[46] & 1)
|
|
mode = 2;
|
|
else
|
|
mode = 0;
|
|
if (buf[57] & 1)
|
|
mode++;
|
|
buf[50] &= 0xc0;
|
|
if (mode == 3)
|
|
buf[49] &= 0xc0;
|
|
if (mode == 1)
|
|
buf[39] &= 0xc0;
|
|
if (mode == 2 || mode == 3)
|
|
buf[38] &= 0xc0;
|
|
buf[46] = (buf[46] & 0xcf) | pan;
|
|
buf[57] = (buf[57] & 0xcf) | pan;
|
|
if (mode == 1 && reverb) {
|
|
unsigned val;
|
|
val = buf[43] & 0x0f;
|
|
if (val > 0)
|
|
val--;
|
|
buf[43] = (buf[43] & 0xf0) | val;
|
|
val = buf[54] & 0x0f;
|
|
if (val > 0)
|
|
val--;
|
|
buf[54] = (buf[54] & 0xf0) | val;
|
|
}
|
|
}
|
|
}
|
|
|
|
void loadfm()
|
|
{
|
|
int sbfd, i, n, voice_size, data_size;
|
|
char buf[60];
|
|
struct sbi_instrument instr;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
fmloaded[i] = 0;
|
|
srand(getpid());
|
|
if (wantopl3) {
|
|
voice_size = 60;
|
|
sbfd = open(O3MELODIC, O_RDONLY, 0);
|
|
} else {
|
|
voice_size = 52;
|
|
sbfd = open(SBMELODIC, O_RDONLY, 0);
|
|
}
|
|
if (sbfd == -1)
|
|
{
|
|
/* error: should quit */
|
|
}
|
|
instr.device = sb_dev;
|
|
|
|
for (i = 0; i < 128; i++) {
|
|
if (read(sbfd, buf, voice_size) != voice_size)
|
|
{
|
|
/* error: should quit */
|
|
}
|
|
instr.channel = i;
|
|
|
|
if (strncmp(buf, "4OP", 3) == 0) {
|
|
instr.key = OPL3_PATCH;
|
|
data_size = 22;
|
|
} else {
|
|
instr.key = FM_PATCH;
|
|
data_size = 11;
|
|
}
|
|
|
|
fmloaded[i] = instr.key;
|
|
|
|
adjustfm(buf, instr.key);
|
|
for (n = 0; n < 32; n++)
|
|
instr.operators[n] = (n < data_size) ? buf[36 + n] : 0;
|
|
|
|
SEQ_WRPATCH(&instr, sizeof(instr));
|
|
}
|
|
close(sbfd);
|
|
|
|
if (wantopl3)
|
|
sbfd = open(O3DRUMS, O_RDONLY, 0);
|
|
else
|
|
sbfd = open(SBDRUMS, O_RDONLY, 0);
|
|
|
|
for (i = 128; i < 175; i++) {
|
|
if (read(sbfd, buf, voice_size) != voice_size)
|
|
{
|
|
/* error: should quit */
|
|
}
|
|
instr.channel = i;
|
|
|
|
if (strncmp(buf, "4OP", 3) == 0) {
|
|
instr.key = OPL3_PATCH;
|
|
data_size = 22;
|
|
} else {
|
|
instr.key = FM_PATCH;
|
|
data_size = 11;
|
|
}
|
|
fmloaded[i] = instr.key;
|
|
|
|
adjustfm(buf, instr.key);
|
|
for (n = 0; n < 32; n++)
|
|
instr.operators[n] = (n < data_size) ? buf[n + 36] : 0;
|
|
|
|
SEQ_WRPATCH(&instr, sizeof(instr));
|
|
}
|
|
close(sbfd);
|
|
}
|
|
#endif /* linux || FreeBSD */
|