1 /***************************************************************************
2  *   S3m/Mod player by Daniel Marks (dmarks@ais.net)
3  *   GUS support by David Jeske (jeske@uiuc.edu)
4  *
5  * (C) 1994,1995 By Daniel Marks and David Jeske
6  *
7  * While we retain the copyright to this code, this source code is FREE.
8  * You may use it in any way you wish, in any product you wish. You may
9  * NOT steal the copyright for this code from us.
10  *
11  * We respectfully ask that you email one of us, if possible, if you
12  * produce something significant with this code, or if you have any bug
13  * fixes to contribute.  We also request that you give credit where
14  * credit is due if you include part of this code in a program of your own.
15  *
16  * Email: s3mod@uiuc.edu
17  *        jeske@uiuc.edu
18  *
19  * See the associated README file for Thanks
20  ***************************************************************************
21  *
22  *  gus.c - Support for the Gravis Ultrasound card using the Voxware(C)
23  *		drivers.
24  */
25 
26 #ifdef GUS
27 
28 #include <sys/soundcard.h>
29 #include <machine/ultrasound.h>
30 #include <unistd.h>
31 #include "gus.h"
32 #include "mod.h"
33 #include "play.h"
34 #include <sys/types.h>
35 #include <stdio.h>
36 #include <fcntl.h>
37 #include "tables.h"
38 #include "main.h"
39 
40 /* /dev/sequencer  information:
41  *
42  * /dev/sequencer was a mystery to me when I began this project
43  * so I will attempt to explain what I have learned about it so far.
44  *
45  * First, you must define a sequencer buffer (SEQ_DEFINEBUF(XXXX))
46  * for your sequencer commands.
47  *
48  * the sequencer commands can be found in "soundcard.h", with some
49  * GUS specific commands being found in "ultrasound.h".
50  * When you want to do stuff with /dev/sequencer, you need to have a
51  * time synchronization so that things happen at the right time.
52  * Convinently, /dev/sequencer provides this as well.
53  * the "sync_time" function, along with "this_time" and "next_time"
54  * sync this program up with /dev/sequencer. You setup your "this_time"
55  * to be now, and then set your next_time into the future and then
56  * sync_time() and it'll wait until time catches up.
57  *
58  * You can see how this is applied if you look in "play_mod_gus" in
59  * play.c. the look where "beattrack" and "update_notes" are called
60  * uses this synchronication. There is no call to "sync_time()" here
61  * because it should be as close as possible to the actual sequencer
62  * commands. There are calls to sync_time() in both update_notes and
63  * prepare_notes.
64  */
65 
66 
67 SEQ_DEFINEBUF(2048);
68 
69 int sample_ok[MAX_SAMPLES];
70 
71 extern int gus_dev;
72 int seqfd;
73 
74 int ticks_per_division;
75 double tick_duration;
76 double this_time,next_time;
77 double clock_rate;
78 
79 
80 unsigned int gus_total_mem=0;
81 
82 /* GUS SETUP VARS
83  * this is sort of a hack, not all things should have these
84  * variables set this way. (i.e. other mod file formats need a
85  * different clock rate) but I'll get to it later
86  */
87 
gus_setup_vars()88 void gus_setup_vars()
89 {
90    clock_rate = 50.0; /* for mods */
91 
92    tick_duration = 100.0 / clock_rate;
93 }
94 
95 /* GUS GET DEVICE
96  * this checks for /dev/sequencer, and then checks to make sure there
97  * is a GUS in there somewhere. If not, then it returns (0) telling
98  * the init routines in "play.c" that there is no GUS available and it
99  * should look for something else.
100  * If it finds a GUS it clears out the RAM on the GUS, and then finds
101  * out how much total free memory is available on it.
102  */
103 
get_gus_device(void)104 int get_gus_device(void)
105 {
106   uint32 i,n;
107 
108   struct synth_info info;
109 
110   if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
111     {
112       /* perror ("/dev/sequencer");*/
113       seqfd = -1;
114       return (0);
115     }
116 
117   printf("seqfd: %d\n",seqfd);
118 
119   if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
120     {
121       /* perror ("/dev/sequencer"); */
122       return (0);
123     }
124 
125   for (i = 0; i < n; i++)
126     {
127       info.device = i;
128 
129       if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
130 	{
131 	  /* perror ("/dev/sequencer"); */
132 	  return (0);
133 	}
134 
135       if (info.synth_type == SYNTH_TYPE_SAMPLE
136 	  && info.synth_subtype == SAMPLE_TYPE_GUS)
137 	gus_dev = i;
138     }
139 
140   if (gus_dev == -1)
141     {
142     /*  fprintf (stderr, "Gravis Ultrasound not detected\n"); */
143       return (0);
144     }
145 
146 /*
147   if ((mixerfd = open ("/dev/mixer", O_RDWR, 0)) == -1)
148     printf ("Mixer not available.\n");
149  */
150 
151 
152   ioctl (seqfd, SNDCTL_SEQ_SYNC, 0);
153   ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev);
154 
155 
156   gus_total_mem = gus_mem_free(gus_dev);
157 
158   return (1);
159 }
160 
161 /* GUS MEM FREE
162  * this checks to see how much memory is available on the
163  * GUS which is on /dev/sequencer
164  */
165 
gus_mem_free(int dev)166 int gus_mem_free (int dev)
167 {
168   int temp = dev;
169   int temp2 = seqfd;
170 
171 #ifdef DEBUG
172   printf("Seqfd = %d\n",seqfd);
173 #endif
174 
175   if (ioctl (temp2, SNDCTL_SYNTH_MEMAVL, &temp)==-1)
176     { printf("ioctl error: int gus_mem_free(int dev)   seqfd,dev = %d,%d\n",
177 	     temp2,dev);
178       exit(1);
179     }
180   return (temp);
181 }
182 
183 unsigned char dump_enabled=1;
184 
185 /* SEQBUF DUMP
186  * borrowed from gmod (see README for thanks)
187  * this dumps all the current
188  * sequencer information to /dev/sequencer
189  */
190 
seqbuf_dump()191 void seqbuf_dump ()
192 {
193   int result;
194 
195   while (_seqbufptr && dump_enabled)
196     {
197       if ((result = write (seqfd, _seqbuf, _seqbufptr)) == -1)
198         {
199           perror ("write /dev/sequencer");
200           exit (ERR_SEQUENCER);
201         }
202       else if (result != _seqbufptr)
203         {
204           _seqbufptr -= result;
205           memmove (_seqbuf, &(_seqbuf[result]), _seqbufptr);
206         }
207       else
208         _seqbufptr = 0;
209     }
210 }
211 
212 /* GUS PATCH LOAD
213  * this loads a patch onto the GUS.
214  * if the patch will not load, then it sets "sample_ok[sample_num]"
215  * to 0. This makes it so the GUS sound support wont try and play
216  * that instrument later.
217  * For some reason, if too many patches fail to load, I think the
218  * kernel drivers get confused.
219  */
220 
gus_patch_load(struct patch_info * patch,int sample_num)221 void gus_patch_load (struct patch_info *patch, int sample_num)
222 {
223   int bytes_read;
224   int len = patch->len;
225   unsigned short loop_flags = patch->mode;
226   int loop_start = patch->loop_start;
227   int loop_end = patch->loop_end;
228   int mem_avail;
229 
230   /* try to remove loop clicking */
231 
232   if ((loop_flags & WAVE_LOOPING) && (loop_end >= 2) &&
233       !(loop_flags & WAVE_16_BITS))
234     {
235       patch->data[loop_end] = patch->data[loop_start];
236 
237       if (loop_flags & WAVE_UNSIGNED)
238 	patch->data[loop_end - 1] =
239 	  ((unsigned char) (patch->data[loop_end - 2]) +
240 	   (unsigned char) (patch->data[loop_end])) / 2;
241       else
242 	patch->data[loop_end - 1] =
243 	  ((signed char) (patch->data[loop_end - 2]) +
244 	   (signed char) (patch->data[loop_end])) / 2;
245     }
246 
247   mem_avail = gus_mem_free (gus_dev);	/* XXX */
248 
249 
250   if (sample_num > MAX_SAMPLES)
251     {
252       printf("*** ERROR: Sample Num:%d is greater than MAX_SAMPLES\n",
253 	     sample_num);
254     }
255   else
256   if (mem_avail < len)
257     {
258       printf ("*** Skipping patch - %d needed, %d available ***\n",
259 	      len, mem_avail);
260       /* set sample to be OFF */
261       sample_ok[sample_num]=0;
262     }
263   else
264     {
265       SEQ_WRPATCH (patch, sizeof (*patch) + len);
266       /* set sample to be ON */
267       sample_ok[sample_num] = 1;
268     }
269 
270 }
271 
272 /* SYNC TIME
273  * borrowed from gmod (See README for thanks)
274  * this is part of the timing mechanism for talking to /dev/sequencer
275  * see the top of this file for a more complete description
276  */
277 
sync_time(void)278 void sync_time(void)
279 {
280    if(next_time > this_time)
281      {
282        SEQ_WAIT_TIME ((long) next_time);
283        this_time = next_time;
284      }
285 
286 }
287 
288 /* STOP ALL VOICES
289  * this stops all the voices which are currently playing on the
290  * GUS.
291  */
292 
stop_all_voices(void)293 void stop_all_voices(void)
294 {
295   int j;
296   GUS_NUMVOICES(gus_dev,32);
297 
298   for(j=0;j<32;j++)
299     {
300       GUS_VOICEOFF(gus_dev,j);
301       GUS_RAMPOFF(gus_dev,j);
302     }
303 
304   SEQ_DUMPBUF();
305 
306 }
307 
308 /* SET PANNING
309  * borrowed from gmod (see README for thanks)
310  * this functions generates a /dev/sequencer panning value from a number
311  * between -127 to 127. It sends that command to the GUS
312  */
313 
set_panning(int channel,signed char panning,unsigned char hw_flag)314 void set_panning (int channel, signed char panning, unsigned char hw_flag)
315 {
316   int pan_val;
317 
318   pan_val = panning * 2;
319   if (pan_val > 127)
320     pan_val = 127;
321   else if (pan_val < -127)
322     pan_val = -127;
323   sync_time ();
324   SEQ_PANNING (gus_dev, channel, (signed char) pan_val);
325   if (hw_flag == PAN_HARDWARE)
326     {
327       pan_val = (pan_val + 16) / 32 + 7;
328       GUS_VOICEBALA (gus_dev, channel, pan_val);
329     }
330 
331 }
332 
333 /* GUS SET SPEED
334  * borrowed from gmod (see README for thanks)
335  * this makes speed changes based on the set speed commands
336  * in MOD and S3M files. if the number is equal to or below
337  * the threshold then it is a "tempo" change, if not, then it's a
338  * "bpm" change. The threshold for MODs is 32, while the
339  * threshold for S3Ms is 48.
340  */
341 
gus_set_speed(int parm)342 void gus_set_speed(int parm)
343 {
344   if (!parm)
345     parm = 1;
346 
347   if (parm < (mod.s3m ? 49 : 33))
348     ticks_per_division = parm;
349   else
350     tick_duration = (250.0 / parm);
351 
352 }
353 
354 /* PERIOD TO NOTE
355  * written by the authors of gmod (see README for thanks)
356  * this routine takes a period for a note and turns it into a midi note/bend
357  * pair. You can see how this note/bend pair is used to generate a
358  * "start note"/"pitchbend" pair of commands in the "prepare_notes" and
359  * "update_notes" command.
360  * this uses a period table which is in "tables.h"
361  */
362 
period_to_note(int period,int * note,int * pitchbend)363 void period_to_note (int period, int *note, int *pitchbend)
364 {
365   int low = 0, middle = 0, high = NUM_PERIODS - 1;
366   int diff, diff_high, diff_low;
367 
368   *pitchbend = 0;
369 
370   if (period > period_table[0])
371     period = period_table[0];
372   else if (period < period_table[NUM_PERIODS - 1])
373     period = period_table[NUM_PERIODS - 1];
374 
375   while (high >= low)
376     {
377       middle = (high + low) / 2;
378       if (period == period_table[middle])
379 	break;
380       else if (period < period_table[middle])
381 	low = middle + 1;
382       else
383 	high = middle - 1;
384     }
385 
386   if (period != period_table[middle])
387     {
388       diff_high = abs (period - period_table[high]);
389       diff_low = abs (period - period_table[low]);
390 
391       if (diff_low < diff_high)
392 	middle = low;
393       else
394 	middle = high;
395 
396       if (period < period_table[middle])
397 	diff = period_table[middle] - period_table[middle + 1];
398       else
399 	diff = period_table[middle - 1] - period_table[middle];
400 
401       *pitchbend = (period_table[middle] - period) * 100 / diff;
402     }
403 
404   *note = middle + NOTE_BASE;
405 }
406 
407 /* PREPARE NOTES
408  * this sets up the notes which will be played during the next row for one
409  * track. If you are all familiar with mods, (or want to be) you should
410  * know that mods can only change the "note" on the beginning of a
411  * row. During that "row", however long it lasts according to the song
412  * tempo, every 1/50 of a second, the effects are updated. When you have
413  * a DSP playing the song, then the computer mixes a new "sample buf"
414  * every 1/50 of a second. On the gus, all you have to do is adjust the
415  * notes every 1/50 of a second. The routine called "update_notes" does
416  * this
417  */
418 
prepare_notes(int channel,track_info_ptr old_track,track_info_ptr track)419 void prepare_notes(int channel,track_info_ptr old_track,track_info_ptr track)
420 {
421   int note=0,vol=0,samp,bend=0;
422   int pitchbender=0,old_note;
423 
424   samp = track->samp;
425 
426 
427   if (!sample_ok[samp])
428     return;
429 
430   period_to_note(old_track->playing_period,&old_note,&bend);
431   period_to_note(track->playing_period,&note,&bend);
432   track->note = note;
433   vol = track->playing_volume;
434 
435   /* if the patch is not loaded yet, then load it */
436 
437   if ((track->note_hit) || (old_track->samp != track->samp))
438     {
439       if (sample_ok[track->samp])
440 	{
441 	  sync_time();
442 	  SEQ_SET_PATCH(gus_dev,channel,track->samp);
443 	}
444     }
445 
446   sync_time();
447   if (sample_ok[samp])
448     {
449 
450       if (channel & 1)
451 	track->panning = -127;
452       else
453 	track->panning = 127;
454 
455       if (old_track->panning != track->panning)
456 	set_panning(channel,track->panning, PAN_HARDWARE);
457 
458 
459       if (track->note_hit)
460 	{
461 	  pitchbender = (note * 100 + bend) - (note * 100);
462 	  sync_time();
463 	  SEQ_START_NOTE(gus_dev,channel,note,vol);
464 
465 	  SEQ_PITCHBEND(gus_dev,channel,pitchbender + 1);
466         }
467       else
468 	{
469 	  /* check the volume and set it if necessary */
470 
471 	  if ((vol != old_track->playing_volume) && sample_ok[track->samp])
472 	    {
473 	      sync_time();
474 	      SEQ_START_NOTE(gus_dev,channel,255,vol);
475 	    }
476 
477 	  pitchbender = (note * 100 + bend) - (old_note * 100);
478 
479 	  sync_time();
480 	  SEQ_PITCHBEND(gus_dev,channel,pitchbender + 1);
481 	}
482 
483     }
484   else
485     GUS_VOICEOFF(gus_dev,channel);
486 
487   if (!track->playing_volume)
488     GUS_VOICEOFF(gus_dev,channel);
489 
490   track->note_hit = 0;
491 }
492 
493 /* UPDATE NOTES
494  * this routine updates the current characteristics of the notes which
495  * are playing on the GUS. Instead of mixing a "sample buffer" and sending
496  * it to the DSP every 1/50 of a second (like a /dev/dsp player does)
497  * the GUS just needs to have it's voice information updated every 1/50
498  * of a second and it does all the rest of the work for us.
499  */
500 
update_notes(int channel,register track_info_ptr track)501 void update_notes(int channel,register track_info_ptr track)
502 {
503   int16 t;
504   int note,bend;
505   int old_note,old_bend;
506   int vol = track->playing_volume;
507   int retrig=0,pitchbender=0;
508 
509   if (!sample_ok[track->samp])
510      return;
511 
512   period_to_note(track->start_period,&old_note,&old_bend);
513 
514   period_to_note(track->playing_period,&note,&bend);
515 
516   sync_time();
517 
518   /* set the volume */
519   SEQ_START_NOTE(gus_dev,channel,255,vol);
520 
521   /* retrigger if necessary */
522   if (track->note_hit)
523     {
524       SEQ_START_NOTE(gus_dev,channel,note,vol);
525       track->note_hit = 0;
526     }
527 
528   pitchbender = (note * 100 + bend) - (old_note * 100);
529 
530   SEQ_PITCHBEND(gus_dev,channel,pitchbender + 1);
531 
532 }
533 
534 
play_mod_gus(int loud)535 void play_mod_gus(int loud)
536 {
537 
538   int16 i;
539   int extra_ticks, tick;
540   track_info_ptr track;
541   track_info_ptr old_track;
542   uint8 *c;
543   uint16 *d;
544 
545 
546   gus_setup_vars();
547 
548   startplaying(loud);
549 
550   stop_all_voices();
551 
552   SEQ_START_TIMER();
553 
554   if (mod.tracks < 14)
555     {
556       GUS_NUMVOICES (gus_dev, 14);
557     }
558   else
559     {
560       GUS_NUMVOICES (gus_dev, mod.tracks);
561     }
562 
563 
564   for(i=0;i<mod.tracks;i++)
565     {
566       SEQ_BENDER_RANGE(gus_dev,i,8191);
567       SEQ_EXPRESSION(gus_dev,i,options.main_volume);
568       SEQ_MAIN_VOLUME(gus_dev,i,100);
569       SEQ_PANNING (gus_dev,i,0);
570       SEQ_PITCHBEND(gus_dev,i,0);
571     }
572 
573 
574   next_time = 0.0;
575   gus_set_speed(mod.tempo);
576   gus_set_speed(mod.bpm);
577 
578   this_time = 0.0;
579   next_time += tick_duration;
580   sync_time();
581 
582   mod_done = 0;
583 
584   while (!mod_done)
585     {
586       /* save the old track info for "prepare_notes" */
587 
588       memcpy(old_tracks,tracks,sizeof(*tracks) * MAX_TRACKS);
589 
590       updatetracks();
591 
592       track = tracks;
593       old_track = old_tracks;
594       this_time = 0.0;  /* reset the time */
595 
596       /* now setup the voices for the current row */
597 
598       for (i=0;i<mod.tracks;i++)
599 	{
600 	  prepare_notes(i,old_track++,track++);
601 	}
602 
603       /* now that the voices are setup, we need to "modulate" them
604 	 as necessary for the different effects */
605 
606       extra_ticks = 0; /* check this out */
607 
608       tempo_wait = tempo;
609 
610       for(tick = 0; tick < ticks_per_division + extra_ticks;tick++)
611 	{
612 	  track = tracks;
613 
614 	  for(i=0;i<mod.tracks;i++)
615 	    {
616 	      beattrack(track);
617 	      update_notes(i,track);
618 	      track++;
619 	    }
620 	  tempo_wait--;
621 	  next_time += tick_duration;
622 	}
623 
624     }
625 
626 }
627 
628 #endif /* GUS */
629