/* Extended Module Player * Copyright (C) 1996-2001 Claudio Matsuoka and Hipolito Carraro Jr * AWE32 support Copyright (C) 1996, 1997 Takashi Iwai * * This file is part of the Extended Module Player and is distributed * under the terms of the GNU General Public License. See doc/COPYING * for more information. * * $Id: oss_seq.c,v 1.3 2001/01/15 03:01:18 claudio Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define USE_SEQ_MACROS #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_PARAM_H #include #endif #if defined(HAVE_SYS_SOUNDCARD_H) #include #elif defined(HAVE_MACHINE_SOUNDCARD_H) #include #endif #if defined(HAVE_SYS_ULTRASOUND_H) #include #elif defined(HAVE_LINUX_ULTRASOUND_H) #include #elif defined(HAVE_MACHINE_ULTRASOUND_H) #include #endif #if defined(HAVE_AWE_VOICE_H) #include #elif defined(HAVE_SYS_AWE_VOICE_H) #include #elif defined(HAVE_LINUX_AWE_VOICE_H) #include #endif #include "xmpi.h" #include "driver.h" #if !defined (DISABLE_AWE) && (defined (HAVE_AWE_VOICE_H) || \ defined (HAVE_SYS_AWE_VOICE_H) || defined (HAVE_LINUX_AWE_VOICE_H)) #define AWE_DEVICE #endif #define SEQ_NUM_VOICES 32 /* #?# **** FIXME **** */ SEQ_DEFINEBUF(2048); static int seqfd = -1; static int echo_msg; static int numvoices (int); static void voicepos (int, int); static void echoback (int); static void setpatch (int, int); static void setvol (int, int); static void setnote (int, int); static void setpan (int, int); static void setbend (int, int); static void seteffect (int, int, int); static void starttimer (void); static void stoptimer (void); static void resetvoices (void); static void bufdump (void); static void bufwipe (void); static void clearmem (void); static void seq_sync (double); static int writepatch (struct patch_info *); static int init (struct xmp_control *); static int getmsg (void); static void shutdown (void); #define seqbuf_dump bufdump static char *help[] = { "awechorus=mode", "Set chorus mode in AWE cards", "awereverb=mode", "Set reverb mode in AWE cards", "dev=", "Device to use (default /dev/sequencer)", "opl2", "Use OPL2 (YM3812) FM synthesizer", NULL }; struct xmp_drv_info drv_oss_seq = { "oss_seq", /* driver ID */ "OSS sequencer", /* driver description */ help, /* help */ init, /* init */ shutdown, /* shutdown */ numvoices, /* numvoices */ voicepos, /* voicepos */ echoback, /* echoback */ setpatch, /* setpatch */ setvol, /* setvol */ setnote, /* setnote */ setpan, /* setpan */ setbend, /* setbend */ seteffect, /* seteffect */ starttimer, /* settimer */ stoptimer, /* stoptimer */ resetvoices, /* resetvoices */ bufdump, /* bufdump */ bufwipe, /* bufwipe */ clearmem, /* clearmem */ seq_sync, /* sync */ writepatch, /* writepatch */ getmsg, /* getmsg */ NULL }; static int dev; static struct synth_info si; static int chorusmode = 0; static int reverbmode = 0; static char *dev_sequencer = "/dev/sequencer"; static int numvoices (int num) { switch (si.synth_subtype) { case SAMPLE_TYPE_GUS: if (num < 14) num = 14; #ifdef AWE_DEVICE case SAMPLE_TYPE_AWE32: #endif if (num > SEQ_NUM_VOICES) return SEQ_NUM_VOICES; GUS_NUMVOICES (dev, num); break; } return num; } static void voicepos (int ch, int pos) { GUS_VOICE_POS (dev, ch, pos); } static void echoback (int msg) { SEQ_ECHO_BACK (msg); } static void setpatch (int ch, int smp) { SEQ_SET_PATCH (dev, ch, smp); } static void setvol (int ch, int vol) { SEQ_START_NOTE (dev, ch, 255, vol); } static void setnote (int ch, int note) { SEQ_START_NOTE (dev, ch, note, 0); } static void seteffect (int ch, int type, int val) { #ifdef AWE_DEVICE if (si.synth_subtype == SAMPLE_TYPE_AWE32) { switch (type) { case XMP_FX_CHORUS: AWE_SEND_EFFECT (dev, ch, AWE_FX_CHORUS, val); break; case XMP_FX_REVERB: AWE_SEND_EFFECT (dev, ch, AWE_FX_REVERB, val); break; case XMP_FX_CUTOFF: AWE_SEND_EFFECT (dev, ch, AWE_FX_CUTOFF, val); break; case XMP_FX_RESONANCE: AWE_SEND_EFFECT (dev, ch, AWE_FX_FILTERQ, val / 16); break; } } #endif } static void setpan (int ch, int pan) { GUS_VOICEBALA (dev, ch, (pan + 0x80) >> 4) } static void setbend (int ch, int bend) { SEQ_PITCHBEND (dev, ch, bend); } static void starttimer () { SEQ_START_TIMER (); seq_sync (0); bufdump (); } static void stoptimer () { SEQ_STOP_TIMER (); bufdump (); } static void resetvoices () { int i; #ifdef AWE_DEVICE if (si.synth_subtype == SAMPLE_TYPE_AWE32) { AWE_CHORUS_MODE (dev, chorusmode); AWE_REVERB_MODE (dev, reverbmode); } #endif for (i = 0; i < SEQ_NUM_VOICES; i++) { SEQ_STOP_NOTE (dev, i, 255, 0); SEQ_EXPRESSION (dev, i, 255); SEQ_MAIN_VOLUME (dev, i, 100); SEQ_CONTROL (dev, i, CTRL_PITCH_BENDER_RANGE, 8191); SEQ_BENDER (dev, i, 0); SEQ_PANNING (dev, i, 0); bufdump (); } } static void bufwipe () { bufdump (); ioctl (seqfd, SNDCTL_SEQ_RESET, 0); _seqbufptr = 0; } static void bufdump () { int i, j; fd_set rfds, wfds; FD_ZERO (&rfds); FD_ZERO (&wfds); do { FD_SET (seqfd, &rfds); FD_SET (seqfd, &wfds); select (seqfd + 1, &rfds, &wfds, NULL, NULL); if (FD_ISSET (seqfd, &rfds)) { if ((read (seqfd, &echo_msg, 4) == 4) && ((echo_msg & 0xff) == SEQ_ECHO)) { echo_msg >>= 8; xmp_event_callback (echo_msg); } else echo_msg = 0; /* ECHO_NONE */ } if (FD_ISSET (seqfd, &wfds) && ((j = _seqbufptr) != 0)) { if ((i = write (seqfd, _seqbuf, _seqbufptr)) == -1) { fprintf (stderr, "xmp: can't write to sequencer\n"); exit (-4); } else if (i < j) { _seqbufptr -= i; memmove (_seqbuf, _seqbuf + i, _seqbufptr); } else _seqbufptr = 0; } } while (_seqbufptr); } static void clearmem () { int i = dev; ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &i); } static void seq_sync (double next_time) { static double this_time = 0; if (next_time == 0) this_time = 0; if (next_time - this_time < 1.0) return; if (next_time > this_time) { #ifdef HZ /* Nils Faerber's hack */ SEQ_WAIT_TIME (next_time * HZ / 100); #else SEQ_WAIT_TIME (next_time); #endif this_time = next_time; } } static int writepatch (struct patch_info *patch) { struct sbi_instrument sbi; if (!patch) { clearmem (); return XMP_OK; } if ((!!(xmp_ctl->outfmt & XMP_FMT_FM)) ^ (patch->len == XMP_PATCH_FM)) return XMP_ERR_TPTCH; patch->device_no = dev; if (patch->len == XMP_PATCH_FM) { sbi.key = FM_PATCH; sbi.device = dev; sbi.channel = patch->instr_no; memcpy (&sbi.operators, patch->data, 11); write (seqfd, &sbi, sizeof (sbi)); return XMP_OK; } SEQ_WRPATCH (patch, sizeof (struct patch_info) + patch->len - 1); return XMP_OK; } static int getmsg () { return echo_msg; } static int init (struct xmp_control *ctl) { int found; char *buf, *token; char **parm; buf = calloc (1, 256); parm_init (); chkparm1 ("awechorus", chorusmode = strtoul (token, NULL, 0)); chkparm1 ("awereverb", reverbmode = strtoul (token, NULL, 0)); chkparm1 ("opl2", ctl->outfmt |= XMP_FMT_FM); chkparm1 ("dev", dev_sequencer = token); parm_end (); if ((seqfd = open (dev_sequencer, O_RDWR)) != -1) { if ((seqfd != -1) && (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &dev) == -1)) { fprintf (stderr, "xmp: can't determine number of synths\n"); return XMP_ERR_DINIT; } } else { if (ctl->verbose > 2) fprintf (stderr, "xmp: can't open sequencer\n"); return XMP_ERR_DINIT; } for (found = 0; dev--; ) { si.device = dev; if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &si) == -1) { fprintf (stderr, "xmp: can't determine synth info\n"); return XMP_ERR_SYNTH; } if (si.synth_type == (ctl->outfmt & XMP_FMT_FM ? SYNTH_TYPE_FM : SYNTH_TYPE_SAMPLE)) { if (si.synth_type != SYNTH_TYPE_FM) { int i = dev; ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &i); i = dev; ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &i); if (!i) continue; ctl->memavl = i; } sprintf (buf, "%s [%s]", drv_oss_seq.description, si.name); #ifdef AWE_DEVICE if (si.synth_subtype == SAMPLE_TYPE_AWE32) { strcat (buf, "\nAWE support Copyright (C) 1996,1997 Takashi Iwai"); #ifdef HAVE_AWE_MD_NEW_VOLUME_CALC AWE_MISC_MODE (dev, AWE_MD_NEW_VOLUME_CALC, 0); #endif } #endif drv_oss_seq.description = buf; found = 1; break; } } if (!found) { close (seqfd); return XMP_ERR_DINIT; } SEQ_VOLUME_MODE (dev, VOL_METHOD_LINEAR); bufdump (); ioctl (seqfd, SNDCTL_SEQ_SYNC, 0); return XMP_OK; } static void shutdown () { close (seqfd); } XMP_DRV_REGISTER(drv_oss_seq);