[comp.sys.hp] Sound, music on 300/400 series

zap@indic.se (Jonas Petersson) (04/04/91)

[I believe this has been discussed before]

From what I've heard, it is possible to get quite good sound out of a
375/400 (and probably other models too) - using XBeep() and changing it's
frequency is not what I would call good... Could someone send me some
information on how to do this.

		Thanks in advance / Jonas

-- 
     ///  "Are you THE Zaphod Beeblebrox ????" # zap@indic.se
__  ///                                        # Air Quality Surveillance
\\\///    "No, just A Zaphod Beeblebrox.       # "I can feel it coming 
 \///  Didn't you hear - I come in sixpacks !" #   in the air..."

ken@hpcupt3.cup.hp.com (Kenneth M. Sumrall) (04/10/91)

>From what I've heard, it is possible to get quite good sound out of a
>375/400 (and probably other models too) - using XBeep() and changing it's
>frequency is not what I would call good... Could someone send me some
>information on how to do this.
>
Here is something I picked up off the net about 3 years ago.  It plays
music in three part harmony from ascii text music files.  Enjoy.

| Ken Sumrall                          |     Internet: ken%hpda@hplabs.hp.com |
| HP California Language Labs          |           UUCP:  ...!hplabs!hpda!ken |
|    "I'd stomp desert dope heads for some gas in my moped!" - Bill the Cat   |
|              "What a stupid world" -Calvin (speaking to Hobbes)             |

#---------------------------------- cut here ----------------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Ken Sumrall <ken@hpclkms> on Tue Apr  9 19:40:07 1991
#
# This archive contains:
#	READ_ME		chop.fant	fug2		play.c		
#	prel3		
#
# Error checking via wc(1) will be performed.
# Error checking via sum(1) will be performed.

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

if sum -r </dev/null >/dev/null 2>&1
then
	sumopt='-r'
else
	sumopt=''
fi

echo x - READ_ME
cat >READ_ME <<'@EOF'
To use this stuff, you need to first compile play.c with

cc -o play play.c -lm

To play something, type:

play < playfile  (e.g. play < chop.fant)


The "playfile" has the basic format of (starting from the first line):

<key sig>
<quarter notes per minute>
<line of music for voice 1>
<line of music for voice 2 or blank line>
<line of music for voice 3 or blank line>
.
.
.

The key signature is in the form "n#" or "nb" where n goes from 0 to 7.
# of course means "sharp", b means "flat".

Quarter note beats per minute is just that; it's in the form  n, where 
n is almost any reasonable number.  Note that 10 milliseconds is the
smallest duration available.

Music lines come in groups of three (3 voices).  A blank line indicates
termination of that group, the other voices are not played.  If all
3 voices are played in a group, DO NOT leave a blank line (bug).

A music line is of the form:  | measure | measure | measure | ...
(blanks must be present around the |).

A measure is just: note note note ...  (blanks must surround notes)

A note is: <pitch>[modifier][octave][length][.][attenuation]

  Pitch is: A B C D E F G R
     R is a rest.  It only needs to be used between notes in a measure
     or between the beginning of the measure and the first note.
     The program doesn't play anything after the last note in a measure,
     but waits for all voices in the 3 measure group to complete.

  Modifier is: b (flat) n (natural) or # (sharp)
     Note that n doesn't currently work (has no effect); if the note
     would be sharped (or flatted) in the key, you must use b or # to
     modify it back to the natural state.  Double-sharp or double-flat 
     is accomplished by just using a sharp or flat on a note which
     would already be sharped or flatted in the current key.
  
  octave is: 0 1 2 3 4 5 6
     Middle C translates to C2.  Some choices (e.g. A0) will be out of
     range and create squawks.
     
  length is: t s e q h w l x d
     t = 32nd note
     s = 16th note
     e = 8th  note
     q, h, w = guess
     l = 12th note
     x = 6th  note
     d = 3rd  note
     

  . means to multiply the note value by 1.5

  attenuation is: 0 1 2 3 4 5 6 7 8 9
    0 is the loudest.

  Only pitch needs to be specified on every note; the other parameters take
  on the same values they had on the previous note.  The note "parsing"
  is somewhat of a hack, so watch out.  The only ambiguity is between
  the octave and attenuation, so if the attenuation is to be changed, the
  octave must be specified.  Other than these 2, the parameters can be
  put in any order (I think).

  There is (yet) no way to handle tied notes.  This creates the biggest
  problem on tied notes across measures.  The . can often be used to handle
  tied notes within measures.

  Speaking of measures, you'll notice that a "time signature" is not 
  used in this program.  You have to keep track of the notes within
  a measure.  The measure concept provides a 'reset' point so that the
  beginning of each measure in the 3 measure group happens at the same
  point in time.  Also, the convention that sharped, flatted, or naturaled
  notes do not change througout the measure is *NOT* adhered to (it's a
  dumb convention anyway).
  
  Have fun, but remember, the program isn't very forgiving!
@EOF
set `sum $sumopt <READ_ME`; if test $1 -ne 46191
then
	echo ERROR: READ_ME checksum is $1 should be 46191
fi
set `wc -lwc <READ_ME`
if test $1$2$3 != 906093307
then
	echo ERROR: wc results of READ_ME are $* should be 90 609 3307
fi

chmod 644 READ_ME

echo x - chop.fant
cat >chop.fant <<'@EOF'
4#
200
| G1w5 |                                |                             |
| G0w5 |  C1l G C2 E C G1 C G C2 E C G1 | C G C2 E C G1 C G C2 E C G1 |

| R2s G2 A G F# G C3 E D C D C B#2 C3 E G | R G2 A G F# G C3 E D C D C B#2 C3 E G |
| C1l G C2 E C G1 E G C2 E C G1           | C1l G C2 E C G1 E G C2 E C G1         |

| Rs A2 C3 D F A C4 D B A G F E D F C | B#3 D4 A3 G F A E D F C B#2 D3 A2 G B A |
| D A C2 F C A1 F C2 D A D C          | G0 D1 F B# F D G0 D1 F B# D  |

| Rs G2 A G F# G C3 E D C D C B#2 C3 E G | R G2 A# G F# G C3 E D C D C B#2 C3 E G |
| C1l G C2 E C G1 E G C2 E C G1         | C G A# E A# G E G A C2 A1 G |

| D E D C# D B A# G F# E4 D C# B3 A G F | A# G B C# E D G A#2 C3 B2 D3 F#2 A G  F# G |
| D G B D2 B1 G D A# C2 F# C A1         | G0 D1 G B G D G0 D1 G B G D |

| G G3 B#2 C3 F2 F3 B#2 C3 E#2 E#3 B#2 C3 F2 F3 B#2 C3 | 
| A0 C1 F A F C A0 C1 F A F C |

| C2 C3 F2 A D D3 F2 A E E3 G2 B G G3 B2 E3 | G2 G3 B#2 C3 F2 F3 B#2 C3 E#2 E#3 B#2 C3 F2 F3 B#2 C3 |
| B0 F1 A B A F E0 B E1 G E B0              | A0 C1 F A F C A0 C1 F A F C |

| E#2 E#3 B2 D3 F2 F3 B2 D3 A2 A3 B2 E3 G2 G3 B2 E3 | G2 G3 B#2 C3 F2 F3 B#2 C3 E#2 E#3 B#2 C3 F2 F3 B#2 C3 |
| B0 D1 A B A D E0 B E1 G E B0              |  A0 C1 F A F C A0 C1 F A F C |

| C2 C3 F2 A D D3 F2 A E E3 G2 B G G3 B2 E3 | G2 G3 B#2 C3 F2 F3 B#2 C3 D D4 F3 A C C4 F3 A |
| B0 F1 A B A F E0 B E1 G E B0 | A C1 F A F C A0 D1 F A F D |
@EOF
set `sum $sumopt <chop.fant`; if test $1 -ne 48646
then
	echo ERROR: chop.fant checksum is $1 should be 48646
fi
set `wc -lwc <chop.fant`
if test $1$2$3 != 285011406
then
	echo ERROR: wc results of chop.fant are $* should be 28 501 1406
fi

chmod 444 chop.fant

echo x - fug2
cat >fug2 <<'@EOF'
3b
75 
|                                    |                                | Re   G3s9 F# Ge  C   E  Gs F# Ge A# | 
| Re C3s9 B#2 C3e G2 A C3s B#2 C3e D | G2 C3s B#2 C3e D F2s G Aq Gs F | E C3 B#2 A# G F E D Ce E3 D C |

| D  Gs  F# Ge A# Cs D Eq Ds Cs     | B2e E3s D Ee G2  A F3s E Fe A#2 | B G3s F Ge B#2 C3 Ds E Fq   |
| B2   A#  B C3  F#2  G A# F#       | Gq  Rs C D E F G Ae. Ds E F     | G A# Be. Es F G A G F E De C3s B#2 |

| Fe Es D C2 B A G Fe A3 G F           | E D E F B#2 C3 D B#2            | C3 Gs F#s Ge D Eq Re E#e    |
| C3q     Rq       Re F3 E D           | Re A2 G F G Fs E Fe D           | Gq Re    B#e C3 Cs B#2 C3e G2 |
| Re C2s9 B#1 C2e G1 A C2s B#1 C2e D   | G1 C2s B#1 C2e D F2s G Aq Gs F  | E C2 B#1 A# G F E D C D E D C B0 A G |
| F Fs E# Fe C Dq Re De                | E Es D Ee B2 C3 Es D Ee F       | B2 E3s D Ee F A2s B C3q B2s A |
| Aq  Re A#e B Bs A# Be F              | Gq Re G A A G F                 | Re A1 B C2 Re A1s G Ae F      |
| F B1 A G F E D C B0 C1 D C B0 A G F  | E1 A G F E Db C B0 Ae C2 B1 A   | G F G A D E F D               |
@EOF
set `sum $sumopt <fug2`; if test $1 -ne 62501
then
	echo ERROR: fug2 checksum is $1 should be 62501
fi
set `wc -lwc <fug2`
if test $1$2$3 != 143191082
then
	echo ERROR: wc results of fug2 are $* should be 14 319 1082
fi

chmod 444 fug2

echo x - play.c
cat >play.c <<'@EOF'
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <math.h>
#include <time.h>
#include <sys/hilioctl.h>
#define hlfstp=1.05946309435

static int atten[3] = {5, 5, 5};
static int oct[3] = {2, 2, 2};
static int time_left[3] = {0, 0, 0};
static int tim_val[3] = {0, 0, 0};
int q_base=75;

int r8042;
static int n_val[9] = {3,6,12,24,48,96,8,16,24};
char *tm_str = "tseqhwlxd";
char *sharp_str = "FCGDAEB";
char *flat_str = "BEADGCF";
int key;                        /* 0 means sharp key, 1 means flat key */

char *sep = " \t";

main(argc, argv)
int argc;
char *argv[];
{

char linebuf[4][160];
int lns_act;
char *tok[3][80];
char *tok_ptr;
int tok_no;
int linepos[3];
int voice;
int new_meas, delay;
int i;
char *key_str = "01234567";

void np_set();
void v_on();
void wait_note();


  if( (r8042=open("/dev/rhil",O_RDWR)) == -1) {
    printf("**Can't open device files\n");
    exit(1);
  }

  if (gets(linebuf[0])==NULL)
    exit(0);

  tok_ptr = strpbrk(key_str, linebuf[0]);
  if (tok_ptr==NULL)
    tok_ptr=key_str;
  i = tok_ptr - key_str;
  if (strchr(linebuf[0], '#')) {
    key = 0;
    *(sharp_str+i)=NULL;
  }
  else {
    key = 1;
    *(flat_str+i)=NULL;
  }

  if (gets(linebuf[0])==NULL)
    exit(0);

  q_base = atoi(linebuf[0]);
  

loop:

  lns_act = 0;
  while ( (lns_act<3) && (gets(linebuf[lns_act]) != NULL) )
    if ( (tok[lns_act][0]=strtok(linebuf[lns_act], sep)) != NULL ) {
      for (tok_no=1; (tok[lns_act][tok_no]=strtok(NULL, sep)) != NULL; tok_no++) ;
      linepos[lns_act] = 0;
      lns_act++;
    }
    else if ( lns_act )
      break;

  if (lns_act==0)
    exit(0);

  for( i=lns_act; i<3; i++) {
    tok[i][0] = NULL;
    linepos[i] = 0;
  }


  while( tok[0][linepos[0]] || tok[1][linepos[1]] || tok[2][linepos[2]] ) {
    
    new_meas = 1;
    for(voice=0; voice<lns_act; voice++)
      new_meas &= !strcmp(tok[voice][linepos[voice]], "|");

    for(voice=0; voice<lns_act; voice++) {
       
       tok_ptr = tok[voice][linepos[voice]];
       if( tok_ptr && strcmp(tok_ptr,"|") && time_left[voice]==0 || new_meas ) {
/*
         np_set(voice, tok[voice][++linepos[voice]]);
         v_on(voice);
*/
         v_on(voice, tok[voice][++linepos[voice]]);
       }
    }

    delay = 0x7fffffff;
    for(voice=0; voice<lns_act; voice++)
      if ((time_left[voice]<delay) && time_left[voice])
        delay=time_left[voice];
       
    if (delay != 0x7fffffff) {
       wait_note(delay);
       for(voice=0; voice<lns_act; voice++)
         if(time_left[voice])
           time_left[voice] -= delay;
    }

  }

  goto loop;

}
         

void v_on (vnum, cnote)
int vnum;
char *cnote;
{
char *notestr, *modstr, *octstr, *attenstr;
char *noteptr, *modptr, *octptr, *attenptr, *tm_ptr;
int bnote, mod, note, tvar;
double count;
int intcount, voice;
    
    if (cnote == NULL)
      return ;

    notestr="C D EF G A B R|";
    modstr="bn#";
    octstr="0123456";
    attenstr="0123456789";


    noteptr=strchr(notestr, *cnote);
    if( noteptr!=NULL ) {
      bnote = noteptr - notestr;
      if(key)
        bnote = bnote - (strchr(flat_str, *noteptr) != NULL);
      else
        bnote = bnote + (strchr(sharp_str, *noteptr) != NULL);
    }
    else
      bnote = 14;

    modptr=strpbrk(modstr, cnote);
    if( modptr!=NULL )
      mod = modptr - modstr - 1;
    else
      mod = 0;

    octptr=strpbrk(cnote, octstr);  /* NOTE ORDER of params in strpbrk */
    if( octptr!=NULL ) {
      oct[vnum] = *octptr - 0x30;
      attenptr = octptr+1;
    }
    else
      attenptr = cnote;

    attenptr = strpbrk(attenptr, attenstr);
    if(attenptr != NULL)
      atten[vnum] = *attenptr - 0x30;
 
/*
    tvar = strlen(cnote)-1;
    if (tvar > 1) {
      attenptr = strchr(attenstr, *(cnote+tvar));
      if(attenptr != NULL)
        atten[vnum] = *attenptr - 0x30;
    }
*/

    tm_ptr=strpbrk(tm_str, cnote);
    if (bnote>=14)
      time_left[vnum]=0;
    else { 
      if (tm_ptr!=NULL)
        tim_val[vnum] = (250 * n_val[tm_ptr-tm_str]) / q_base;
      if (strpbrk(".", cnote))
        tim_val[vnum] = (tim_val[vnum]*3)/2;
      time_left[vnum] = tim_val[vnum];
    }


    if (bnote <= 12) {
       note = oct[vnum]*12 + (bnote+3) + mod;  /* 3 is added so that note 0
                                                  can be used for 'A' (55HZ) */

       count = 83333.0 / (55.00 * pow(hlfstp, (double)note));

                                              /* Convert double to integer */
       intcount = (int) count;

       voice = (time_left[vnum]) | 
               ((0x90+vnum*32+atten[vnum]) << 8) |
               ((intcount>>4) << 16) |
               ((0x80+vnum*32+intcount%16) << 24) ;
       ioctl(r8042, EFTSBP, &voice);
    }
}

void wait_note(time)
int time;                           /* time is in 10's of milliseconds */
{
struct timeval timeout;

   timeout.tv_sec  = (time/100);
   timeout.tv_usec = (time%100)*10000;      /* 300 has a min wait of 20 milliseconds */
   select(0, 0, 0, 0, &timeout);

}
@EOF
set `sum $sumopt <play.c`; if test $1 -ne 18955
then
	echo ERROR: play.c checksum is $1 should be 18955
fi
set `wc -lwc <play.c`
if test $1$2$3 != 2265815005
then
	echo ERROR: wc results of play.c are $* should be 226 581 5005
fi

chmod 644 play.c

echo x - prel3
cat >prel3 <<'@EOF'
7#
150
| E3s9 C  G2  C3  E  C  | F  C  F  C  F  C  | G  C  G  C  G  C  | A  C  A  C  A  C  | G  C  G  C  G  C  |
| C1q9           C2e   | D1q         C2e   | E1q         C2e   | F1q         C2e   | E1q         C2e   |
| C0e9           C1e   | D0e         D1e   | E0e         E1e   | F0e         F1e   | E0e         E1e   |

| F   E  D  E  F  D    | E  D  C  D  E  C  | D  E  D  C  B2  A  | G2q2        G3e   | A2q             G3e     |
| D1q          C2e     | C1q         C2e   | C2e  B1s0 A  G  A  | B  G  D  G  B  G  | C2  G1  C2  G1  C2  G1  |

| B2q             G3e     | Cq              Ge      | B2q             G3e     | A2q           F#3e    |
| D2  G1  D2  G1  D2  G1  | E2  G1  E2  G1  E2  G1  | D2  G1  D2  G1  D2  G1  | C2  B1  A  B  C2  A1  |

| G2q             G3e     | G3e   F3s0  E   D   E   |
| B   A   G   A   B   G   | A   Bb  A   G   F   E   |

| F3s Ds A2s D3s Fs Ds | Gs Ds Gs Ds Gs Ds | As Ds As Ds As Ds | Bbs Ds Bbs Ds Bbs Ds | As Ds As Ds As Ds |
| D1q2           D2e   | E1q         D2e   | F1q         D2e   | G1q         D2e      | F1q         D2e   |

| Gs  Fs Es Fs Gs Es   | Fs Es Ds Es Fs Ds | Es Fs Es Ds Cs B2s | A2q2          A3e     | B2q             A3e     |
| E1q          C#2e    | D1q         D2e   | De  C2s0 B1s As Bs | C2s A1s Es As C2s A1s | D2s A1s D2s A1s D2s A1s |

| C3q             A3e     | Dq              Ae      | Cq              A3e     | B2q            G#3e   |
| E2s A1s E2s A1s E2s A1s | F2s A1s F2s A1s F2s A1s | E2s A1s E2s A1s E2s A1s | D2s Cs B1s C2s Ds B1s |

| A2q           A3e       | A3e0     G3e      F#e     |
| C2s B1s As Bs C2s A1s   | D#2s C#s B1s C#2s D#s B1s |

@EOF
set `sum $sumopt <prel3`; if test $1 -ne 51957
then
	echo ERROR: prel3 checksum is $1 should be 51957
fi
set `wc -lwc <prel3`
if test $1$2$3 != 273641639
then
	echo ERROR: wc results of prel3 are $* should be 27 364 1639
fi

chmod 444 prel3

exit 0