[comp.sys.hp] Guts of the tone generator on the 320 system

chris@ic.UUCP (04/22/87)

We spent last night playing with the tone generator on our 320s.
Although we were able to get a number of unique and interesting
noises out of them, we couldn't figure out the layout of the
four bytes in the EFTSBP ioctl command (other than the volume and
timer bits).  Can anyone post the layout of these bytes.  Thanks!

(The EFTSBP is the ioctl command for the rhil device which sends
four data bytes to the tone generator.  Beyond that, none of my
documentation details the purpose of those bytes).
	Chris Guthrie
	U.C.B.

marvit@hplabsc.UUCP (Peter Marvit) (04/23/87)

<beeper taming on HP9000/300>

I have some code written by a colleague which explains the tone generator
and provides and example which plays "Baby Elephant Walk." The shar file is
21K (with author's names carefully expunged).

I will mail it to those who are interested, or will post it in
net/comp.sources if I get a flood of requests.

-Peter Marvit
 HP Labs
 ARPA: marvit@hplabs.hp.com    uucp: {seismo,ihnp4,decvax,etc}!hplabs!marvit

marvit@hplabsc.UUCP (Peter Marvit) (04/23/87)

<someone else actually posted the source to music for the beeper>

Thanks Gary.  That was the same code I had.  I recieved about 12 requests,
which should be satisfied with that posting.

-Peter Marvit
 HP Labs
 etc...

tgl@zog.cs.cmu.edu (Tom Lane) (04/23/87)

In <1365@ucbcad.berkeley.edu> chris@ic.uucp (Chris Guthrie) writes:
>We spent last night playing with the tone generator on our 320s.
>Although we were able to get a number of unique and interesting
>noises out of them, we couldn't figure out the layout of the
>four bytes in the EFTSBP ioctl command (other than the volume and
>timer bits).  Can anyone post the layout of these bytes.  Thanks!

This may not be too accurate 'cuz its based on old documentation
(pre-Series 300) for a different operating system (HP Pascal),
but it's worth a try.

What you have is a TI SN76494 4-voice sound generator; it can make
3 independent square waves plus a noise source.  To control one of
the square wave voices, the format of the 4 bytes is:

byte 1:   1 v1 v0  0 f3 f2 f1 f0
byte 2:   0  0 f9 f8 f7 f6 f5 f4
byte 3:   1 v1 v0  1 a3 a2 a1 a0
byte 4:  d7 d6 d5 d4 d3 d2 d1 d0

where v1-v0 is the voice number (00,01, or 10);
f9..f0 is a 10-bit value defining the frequency;
a3..a0 is a 4-bit value defining the attenuation (volume);
d7..d0 is an 8-bit value defining the duration of the tone.

Note that the voice number must be given in two places.
The f9..f0 value should be 83333 / (desired freq. in Hz);
the a3..a0 value is 0 (maximum volume) to 15 (off), in steps of about 2 dB;
the d7..d0 value is duration in 10-msec units, or 0 to sound
until a countermanding command is given.

For the noise source, the command format is:

byte 1:   1  1  1  0  0 fb n1 n0
byte 2:   1  1  1  0  0 fb n1 n0	(i.e. same as byte 1)
byte 3:   1  1  1  1 a3 a2 a1 a0
byte 4:  d7 d6 d5 d4 d3 d2 d1 d0

(thus, the voice number is "11") where:
fb is 0 for "periodic" noise and 1 for white noise;
n1-n0 control the "noise generator shift rate" as follows:
00  333333/64 Hz
01  333333/128 Hz
10  333333/256 Hz
11  Use voice "10"'s output
(don't ask me what any of that means).
The attenuation and duration are the same as for the other sources.

Somebody please check this out and let the net know if it's OK...
I don't run HP/UX.

				tom lane
-----
ARPA: lane@ZOG.CS.CMU.EDU
UUCP: ...!seismo!zog.cs.cmu.edu!lane
BITNET: lane%zog.cs.cmu.edu@cmuccvma

fritz@hpfclp.HP.COM (Gary Fritz) (04/24/87)

Here is some ancient C and PSL (Portable Standard Lisp) code out of my 
packrat's archive to serve as a a starting point.  It includes a some
documentation, driver code, and a sample song (Baby Elephant Walk).  
The PSL is not supported on the HP Common Lisp product, so you will
have to massage it some.  One of these days I should just convert the thing 
to Common Lisp...

I did not write this code, and have not really done much with it.  I tried the
C code once a long time ago and it seemed to work.  You are welcome to it, 
but you are on your own as far as support and questions go.  Also, be aware 
that this is not an officially support feature, and may change in future 
products.  No warrantee expressed or implied.  Your mileage may vary.

Enjoy!

Gary Fritz
{ihnp4,hplabs}!hpfcla!fritz



#!/bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Contents:  README Makefile dbsetup.sl music.c beeper.c mytune.h
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'

For those who my not know, the beeper on gators/bobcats have
four independent voices. Three of these have a programmable tone, 
and the fouth is a noise generator. 

Someone wrote some low level code to talk to the beeper.
I wrote some PSL code that allows you to write music in an 
easy to read format (See example in the file dbsetup.sl). More 
recently, I translated the functions into C and wrote a function
that would take the lisp lists and generate C data structures 
that could be interpreted by the C code.

Anyone who is running Horizon (nmode? prism? whatever...) should
be able to write music immediately.  Everyone else can build the
example C program and hear what the machine can do. Perhaps 
someone without nmode will be inspired to write a nicer front
end. 

@//E*O*F README//
chmod u=rw,g=r,o=r README
 
echo x - Makefile
sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'

it: music

music.o: music.c mytune.h
	cc -c music.c

beeper.o: beeper.c
	cc -c beeper.c

music: music.o beeper.o
	cc -o music music.o beeper.o

shar:
	shar -c README Makefile dbsetup.sl music.c beeper.c mytune.h > music.shar


@//E*O*F Makefile//
chmod u=rw,g=rw,o=r Makefile
 
echo x - dbsetup.sl
sed 's/^@//' > "dbsetup.sl" <<'@//E*O*F dbsetup.sl//'

%Get Compiled C code

(lisp:load-ofile "/usr/local/src/nmode/beeper/beeper.o" :libs "")
(lisp:ff beep "_beep" :parms (nil nil nil nil))
(lisp:ff noise "_noise" :parms (nil nil nil nil))
(lisp:ff beep-or-noise-duration-left "_beep_or_noise_left" :parms (nil))

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Set up tempo variable and note values
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% (when (null (find-package "music"))
%   (make-package "music"))
%			  
% (setf music::old-package *package*)
% (in-package 'music)
% 
% (import 'pnth (find-package "music"))



(setf tempo 100)

(setf whole 1000)
(setf half (/ whole 2))
(setf quarter (/ half 2))
(setf eighth (/ quarter 2))
(setf sixteenth (/ eighth 2))
(setf thirtysecond (/ sixteenth 2))
(setf dot-quarter (+ quarter eighth))
(setf dot-half (+ half quarter))
(setf dot-eighth (+ eighth sixteenth))
(setf triplet (/ quarter 3))

(setf rest      -1)
(setf  r        -1)
(setf  C         0)
(setf  C-sharp   1)
(setf  cs        1)
(setf  D         2)
(setf  ds        3)
(setf  E-flat    3)
(setf  Ef        3)
(setf  E         4)
(setf  F         5)
(setf  F-sharp   6)
(setf  Fs        6)
(setf  G         7)
(setf  G-sharp   8)
(setf  Gs        8)
(setf  A-flat    8)
(setf  Af        8)
(setf  A         9)
(setf  B-flat   10)
(setf  Bf       10)
(setf  B        11)

(setf tw whole)
(setf th half)
(setf tq quarter)
(setf te eighth)
(setf ts sixteenth)
(setf ts1 (+ ts 1))
(setf tdh dot-half)
(setf tdq dot-quarter)
(setf tde dot-eighth)
(setf tt triplet)

(setf scale
%  (vector
%    (vector   1 510 770 896 959 992)
%    (vector  50 540 784 902 962 994)
%    (vector 100 570 798 909 966 996)
%    (vector 150 600 811 914 970 998)
%    (vector 200 630 823 922 973 999)
%    (vector 250 650 837 928 975 1000)
%    (vector 300 670 849 934 978 1001)
%    (vector 350 690 857 939 981 1002)
%    (vector 390 710 866 945 984 1003)
%    (vector 420 723 874 950 986 1004)
%    (vector 450 740 882 953 988 1005)
%    (vector 480 760 891 956 990 1006)
%    )
  (vector
    (vector   0 386 705 864 943 984)% c
    (vector   0 422 722 873 948 0)  % c#
    (vector   0 456 739 881 952 0)  % d
    (vector   0 487 755 889 956 0)  % d#
    (vector  12 518 770 897 960 0)  % e
    (vector  69 546 784 904 963 0)  % f
    (vector 123 573 798 911 967 0)  % f#
    (vector 173 598 810 916 970 0)  % g
    (vector 220 622 822 922 973 0)  % g#
    (vector 266 645 834 928 976 0)  % a
    (vector 308 665 844 933 978 0)  % a#
    (vector 348 685 854 938 981 0)  % b
    )
  )

(de get-note (pitch octave)
  (let ((real-pitch (+ pitch pitch-offset))
        (real-octave (+ octave octave-offset))
        )
  (when (>= real-pitch 12)
    (setf real-pitch (- real-pitch 12))
    (setf real-octave (+ real-octave 1))
    )

  (when (< real-pitch 0)
    (setf real-pitch (+ real-pitch 12))
    (setf real-octave (- real-octave 1))
    )

  (vector-fetch (vector-fetch scale real-pitch) real-octave)
  ))

% (de get-note (pitch octave)
%  (vector-fetch (vector-fetch scale pitch) octave)
%  )

(de note (pitch octave voice duration)
  (if (eq pitch -1) % it is a rest;
    (beep voice 0 0 (/ (* 10 duration) tempo))
    % otherwise;
    (beep voice (get-note pitch octave) vol (/ (* 10 duration)
					     tempo))
    ))

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Set-up Musical Environment
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

(setq vol 11) % Volume Setting
(setf octave-offset 0)
(setf pitch-offset 0)

%Last note finished, now go on  
(defun go-on ()
  (repeat (beep-or-noise-duration-left 1)
	  (= (beep-or-noise-duration-left 1) 0))
  )

(defun go-on-voice (voice-num)
  (repeat (beep-or-noise-duration-left voice-num)
	  (= (beep-or-noise-duration-left voice-num) 0))
  )

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Music structure:
%
% For each voice, there are parallel lists for the notes, the octaves,
% and the times;
% 
% A stanza is a list of either 3,6,9,or 12 lists; (depending on how many
% voices you use;
%
% sample stanza:
%
% (setf stanza
%  (list (list   a      b  b-flat f-sharp)
%	 (list   1      1    2      1  )
%	 (list whole whole half  quarter)
%	 
%	 (list   a      b  b-flat   g  )
%	 (list   2      2    3      2 )
%	 (list whole  whole half  quarter)
%	 )
%   )
%
% Music task structure:
%
% [                 element #
%   time-to-go         0
%   voice-num          1
%   ( notes )          2
%   ( octave )         3
%   ( time )           4
% ]
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

(defun play-stanza (stanza)
  (let ((time-to-go-idx 0)
	(voice-num-idx  1)
	(notes-idx      2)
	(octaves-idx    3)
	(times-idx      4)
	
	(queue nil)
	(voice-num nil)
	(wait-time nil)
	(temp nil)
	)
    
    % translate music symbols to numbers;
%    (for (in list stanza) (do
%      (for (on i list) (do
%        (when (null (numberp (car i)))
%          (setf (car i) (cdr (assoc (car i) music-symbols)))
%	  )
%	))
%      ))
			   
    % build a task for each voice;
    
    (setf voice-num 1)
    (while stanza
      (push (vector 0
		    voice-num
		    (first stanza)
		    (second stanza)
		    (third stanza)
		    )
	    queue)
      (setf voice-num (+ voice-num 1))
      (setf stanza (pnth stanza 4))
      )

    % while there are voices to be played;
    
    (while queue
      % give more data to voices that are ready;
      (for (in task queue) (with notes octaves times time-to-go) (do

        % a voice ready for another note;
        (when (eq 0 (vector-fetch task time-to-go-idx))
	  (setf notes    (vector-fetch task notes-idx))
	  (setf octaves  (vector-fetch task octaves-idx))
	  (setf times    (vector-fetch task times-idx))
	  (setf time-to-go (first times))

          % play a note on this voice;

          (note (first notes) (first octaves)
		(vector-fetch task voice-num-idx) time-to-go)
	  (vector-store task time-to-go-idx time-to-go)
	  (vector-store task octaves-idx (rest octaves))
	  (vector-store task times-idx (rest times))
	  (vector-store task notes-idx (rest notes))
	  )
	))
      
      % find the task with the shortest time and we'll decrement
      % the other tasks by its time and wait on it;
      
      (setf wait-time 10000)
      (for (in task queue) (do
        (when (< (setf temp (vector-fetch task time-to-go-idx)) wait-time)
	  (setf wait-time temp)
	  (setf voice-num (vector-fetch task voice-num-idx))
	  )
	))

      % update the time of all tasks;
      
      (for (in task queue) (do
        (vector-store task time-to-go-idx
		      (- (vector-fetch task time-to-go-idx) wait-time)
		      )
	))
      
      % wait on the voice that will be done first;
      
      (go-on-voice voice-num)

      % remove voices with no more notes;

      (setf temp queue)
      (for (in task temp) (do
        (when (null (vector-fetch task notes-idx))
	  (setf queue (delete task queue))
	  )
	))
      )
    )) % end of function play-stanza;

% (setf stanza1 (list
%  % first trumpet
%  (list e  f  fs g  e  e  d  cs cs a  af g  fs d  d  c  b  b)
%  (list 1  1  1  1  2  2  2  2  2  1  1  1  1  2  2  2  1  1)
%  (list te te te tq tq te tq tq th te te te tq tq te tq tq th) 
%
%  % second trumpet
%  (list c  d  ef e  g  g  f  e  e  r  r  r  r  r  r  r  r  r)
%  (list 1  1  1  1  1  1  1  1  1  1  1  1  0  1  1  1  1  1)
%  (list te te te tq tq te tq tq th te te te tq tq te tq tq th) 
%  ))
%(setf stanza2 (list
%  (list g  fs f  e  g  c  a  c  d  g  bf ef c  ef f  g  r     g  g  a  g  d  c)
%  (list 1  1  1  1  1  2  1  2  2  1  1  2  2  2  2  2  2     2  2  2  2  2  2)
%  (list te te te te te te tq te tq te te te tq te tq tw tq    te te te te tq te)
%  ))
%
%(let ()
%  (setf tempo 100)
%  (play-stanza stanza1)
%  (play-stanza stanza2)
%  )

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   
%                Baby Elephant Walk
%
%  from the Paramount Picture "HATARI"
%  (c) 1961, 1962, 1968
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% melody;
(setf stanza1 (list
(list f   a   c  f  a  g  f  d  b  c  r          f   a   c  f  a  g  f  d)
(list 2   2   3  3  3  3  3  3  2  3  0          2   2   3  3  3  3  3  3)
(list tde ts1 te te te te te te te te tdh        tde ts1 te te te te te te)

%harmony;
(list c  gs a  c  gs a  c  gs a  c  c  gs a     c  gs a  c  gs a)
(list 1  0  0  1  0  0  1  0  0  1  1  0  0     1  0  0  1  0  0)  
(list tq te te tq te te tq te te te te te te    tq te te tq te te)

(list f  gs a  f  gs a  f  gs a  f  f  gs a     f  gs a  f  gs a)
(list 0  0  0  0  0  0  0  0  0  0  0  0  0     0  0  0  0  0  0)  
(list tq te te tq te te tq te te te te te te    tq te te tq te te)
 ))


%melody;
(setf stanza2 (list
(list c   d   af           d  af f   r)
(list 3   3   2            3  2  2   0)
(list tdh te (+ te tdh te) te te te  tdh)

%harmony;
(list c  c  gs a  c  cs d    f  f  cs d  f  f  cs d    f  f  cs d  f  f  cs d)
(list 1  1  0  0  1  1  1    1  1  1  1  1  1  1  1    1  1  1  1  1  1  1  1)
(list te te te te tq te te   te te te te te te te te   te te te te te te te te)

(list f  f  gs a  f  cs d    bf bf cs d  bf bf cs d    bf bf cs d  bf bf cs d)
(list 0  0  0  0  0  1  1    0  0  1  1  0  0  1  1    0  0  1  1  0  0  1  1)
(list te te te te tq te te   te te te te te te te te   te te te te te te te te)
 ))

%melody;
(setf stanza3 (list
(list f   a   c  f  a  g  f  d  b  c   r  r        c  g  g  e  c  r  c)
(list 2   2   3  3  3  3  3  3  2  3   0  0        3  3  3  3  3  0  3)
(list tde ts1 te te te te te te te te  th te       te tq tq te te te te)

%harmony;
(list c  gs a  c  gs a    c  gs a  c  c  ds e     g  g  ds e  g  g  ds e)
(list 1  0  0  1  0  0    1  0  0  1  1  1  1     1  1  1  1  1  1  1  1)
(list tq te te tq te te   tq te te te te te te    te te te te te te te te)

(list f  gs a  f  gs a    f  gs a  f  f  ds e     c  c  ds e  c  c  ds e)
(list 0  0  0  0  0  0    0  0  0  0  0  1  1     1  1  1  1  1  1  1  1)
(list tq te te tq te te   tq te te te te te te    te te te te te te te te)
 ))

%melody;
(setf stanza4 (list
(list f  f  ef f   ef c   bf gs    b  b  bf b   bf af  f  c    ef f)
(list 3  3  3  3   3  3   2  2     2  2  2  2   2  2   2  2    2  2)
(list tq tq ts ts1 ts ts1 te te    tq tq ts ts1 ts ts1 te te   te te)

%harmony;
(list f  f  cs d  f      c  c  gs a  c     c)
(list 1  1  1  1  1      1  1  0  0  1     1)
(list te te te te th     te te te te th    tq)

(list bf bf cs d  bf     f  f  gs a  f     f)
(list 0  0  1  1  0      0  0  0  0  0     0)
(list te te te te th     te te te te th    tq)
 ))



(let ()
  (setf octave-offset 0)
  (setf pitch-offset 0)
  (setf vol 10)
  (setf tempo 80)
  (play-stanza stanza1)
  (play-stanza stanza2)
  (play-stanza stanza3)
  (play-stanza stanza4)
  )

(setf stanza-counter 0)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% generate C structures to play the tune;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(de print-stanza (stanza stanza-name)
  (let ((notes nil)
	(octaves nil)
	(durations nil)
        (voice-names (make-vector 3 "NULL"))
        (voice-num 0)
	)

    (while stanza
      (vector-store voice-names voice-num (bldmsg "&voice%d" stanza-counter))

      (setf notes (first stanza))
      (setf octaves (second stanza))
      (setf durations (third stanza))
      (setf stanza (pnth stanza 4))
  
      (printf "int notes%d[] =%n  {" stanza-counter)
      (for (in i notes) (do
        (printf "%d," i)
	))
      (printf "-42};%n")
      
      (printf "int octaves%d[] =%n  {" stanza-counter)
      (for (in i octaves) (do
        (printf "%d," i)
	))
      (printf "-42};%n")
      
      (printf "int durations%d[] =%n  {" stanza-counter)
      (for (in i durations) (do
        (printf "%d," i)
	))
      (printf "-42};%n")

      (printf "struct voice_def voice%d = { notes%d, octaves%d, durations%d};%n"
	      stanza-counter stanza-counter stanza-counter stanza-counter);

      (setf stanza-counter (+ stanza-counter 1))
      (setf voice-num (+ voice-num 1))
      )

    (printf "struct stanza_def %s = { %s, %s, %s, %s};%n"
	    stanza-name (vector-fetch voice-names 0)
	                (vector-fetch voice-names 1)
	                (vector-fetch voice-names 2)
	                (vector-fetch voice-names 3))

    ))

% (in-package old-package)
@//E*O*F dbsetup.sl//
chmod u=rw,g=rw,o=r dbsetup.sl
 
echo x - music.c
sed 's/^@//' > "music.c" <<'@//E*O*F music.c//'

#include <stdio.h>

#define tempo 80

struct voice_def {
  int *notes;
  int *octaves;
  int *durations;
  };

struct stanza_def {
  struct voice_def *voice1;
  struct voice_def *voice2;
  struct voice_def *voice3;
  struct voice_def *voice4;
};

struct voice_task {
  int time_to_go;
  int voice_num;
  int *notes;
  int *octaves;
  int *durations;
};

#include "mytune.h"

int vol           = 11;
int octave_offset = 0;
int pitch_offset  = 0;

int scale[12][6] = { 0, 386, 705, 864, 943, 984,
	           0, 422, 722, 873, 948, 0,
	           0, 456, 739, 881, 952, 0,
	           0, 487, 755, 889, 956, 0,  
	          12, 518, 770, 897, 960, 0,
	          69, 546, 784, 904, 963, 0,
	         123, 573, 798, 911, 967, 0,
	         173, 598, 810, 916, 970, 0,
	         220, 622, 822, 922, 973, 0,
	         266, 645, 834, 928, 976, 0,
	         308, 665, 844, 933, 978, 0,
	         348, 685, 854, 938, 981, 0};

beep_or_noise_duration_left(val)
int val;
{
  beep_or_noise_left(val);
}

get_note(pitch,octave)
int pitch,octave;
{
  int real_pitch;
  int real_octave;

  real_pitch  = pitch + pitch_offset;
  real_octave = octave + octave_offset;

  if (real_pitch >= 12) {
    real_pitch  = real_pitch - 12;
    real_octave = real_octave + 1;
  }

  if (real_pitch < 0) {
    real_pitch  = real_pitch + 12;
    real_octave = real_octave - 1;
  }

  return(scale[real_pitch][real_octave]);
}

note(pitch, octave, voice, duration)
int pitch, octave, voice, duration;
{
  if (pitch == -1)
    beep(voice,0,0,(10 * duration)/tempo);
  else
    beep(voice, get_note(pitch,octave), vol, (10 * duration)/tempo);
}

go_on_voice(voice_num)
int voice_num;
{
  while(beep_or_noise_duration_left(voice_num))
    ;
}

load_voice(task,data,voice_num)
struct voice_task *task;
struct voice_def *data;
int voice_num;
{
  if (data == NULL)
    (*task).time_to_go = -1;
  else {
    (*task).time_to_go = 0;
    (*task).voice_num = voice_num;
    (*task).notes = (*data).notes;
    (*task).octaves = (*data).octaves;
    (*task).durations = (*data).durations;
  }
}

play_stanza(stanza)
struct stanza_def *stanza;
{
  struct voice_def *voice1,*voice2,*voice3,*voice4;
  struct voice_task voices[4];
  int num_voices, note_id, octave, voice_num, duration, wait_time;
  int i,j;

  /** load up the voice task structures **/
  load_voice(&voices[0],(*stanza).voice1,1);
  load_voice(&voices[1],(*stanza).voice2,2);
  load_voice(&voices[2],(*stanza).voice3,3);
  load_voice(&voices[3],(*stanza).voice4,4);

  /** how many voices do we have data for? **/
  for (i=0; i<4; i++) 
    if (voices[i].time_to_go == -1)
      break;  

  num_voices = i;
  /** while we still have some voices to give data to **/
  while (num_voices > 0) {
        
    /** find voices that are ready for another note **/  
    for (i=0; i<num_voices; i++)
      if (voices[i].time_to_go == 0) {
        note_id   = *(voices[i].notes++);
        octave    = *(voices[i].octaves++);
        duration  = *(voices[i].durations++);
        voice_num = voices[i].voice_num;
        note(note_id, octave, voice_num, duration);
        voices[i].time_to_go = duration;
      }

    /** find the task with the shortest time and we'll decrement **/
    /** the other tasks by its time and wait on it;              **/

    wait_time = 10000;
    for (i=0; i<num_voices; i++)
      if (voices[i].time_to_go < wait_time) {
        wait_time = voices[i].time_to_go;
        voice_num = voices[i].voice_num;
      }

    /** update the time of all tasks **/

    for (i=0; i<num_voices; i++)
      voices[i].time_to_go -= wait_time;
    
    /** wait on the voice that will be done first **/
    go_on_voice(voice_num);

    /** remove voices with no more notes; **/

    i = 0;
    while(i < num_voices) {
      if (*(voices[i].notes) == -42) {
        /** delete voice # i **/
        for (j=i; j<num_voices-1; j++)
          voices[j] = voices[j+1];
          num_voices--;
      }
      else 
        i++;
    }
  }
}

main()
{
  play_stanza(&stanza1);
  play_stanza(&stanza2);
  play_stanza(&stanza3);
  play_stanza(&stanza4);
}

@//E*O*F music.c//
chmod u=rw,g=rw,o=r music.c
 
echo x - beeper.c
sed 's/^@//' > "beeper.c" <<'@//E*O*F beeper.c//'
/*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; File:         beeper.c
; SCCS:         %A% %G% %U%
; Description:  Access Gator/Bobcat beeper
; Created:      2-Aug-85
; Modified:     7-Aug-85 17:00:34 
; Language:     C
; Package:      PSL
; Status:       Experimental (Do Not Distribute)
;
; (c) Copyright 1985, Hewlett-Packard Company, all rights reserved.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*/

/* Public functions:

       beep
       noise
       beep_or_noise_left

   We offer three voices and a noise source. Each sound is controllable
   in pitch, volume and duration. Pitch goes from 0 to 1023, volume goes
   from 0 to 15, duration is between 0 and 255 10msec intervalls. A
   duration of 0 turns the voice on continuously. A volume of 0 turns
   it off.
   The manufacturing specs give details
   on the programming interface. Here is a summary:

   The beeper is accessed through ioctl calls. The request argument is
   either "Send data to beeper" or "Read voice values from beeper". The
   argument is a pointer to a 4 byte buffer. These four bytes
   are defined here.

   R0-R3: Register address field. In the order R2, R1, R0:
     
     0 0 0: Voice 1 frequency
     0 0 1: Voice 1 attenuation
     0 1 0: Voice 2 frequency
     0 1 1: Voice 2 attenuation
     1 0 0: Voice 3 frequency
     1 0 1: Voice 3 attenuation
     1 1 0: Noise control
     1 1 1: Noise attentuation

  F0-F9: 10 bits pitch
  A0-A3: Attenuation
  D0-D7: Duration in 10msec's

  The placement of data in the buffer is a bit srewy:

  Byte 0 (Frequency 1):  1 R2 R1 R0 F3 F2 F1 F0     LSB
  Byte 1(Frequency 2):  0  0 F9 F8 F7 F6 F5 F4
  Byte 2 (Attenuator) :  1 R2 R1 R0 A3 A2 A1 A0
  Byte 3 (Duration)   : D7 D6 D5 D4 D3 D2 D1 D0

  The volume is inversely proportional to the attenuation. In order
  to provide rising numbers for rising loudness to the user, we
  expect a volume and modify to get the attenuation. The same goes
  for the pitch. In order to calculate frequency of the pitch,
  use: 

           83333/(pitch-1023)

  It is possible at any time to request the time any voice has
  left to run. This is done by:
 
  F4: Read voice1 timer
  F5: Read voice2 timer
  F6: Read voice3 timer
  F7: Read voice4 timer (noise)

  Noise is generated using a shift register. The following controls
  are possible for noise:
 
  - Attenuation
  - Duration
  - Periodic or white noise
  - 3 shift rates or output of voice 4 as shift rate

  Bytes 0 and 1 of the data buffer must both have identical contents
  to control the noise. Attenuation and duration are as in the other
  voices. Bytes 0 and 1 should look like this:

  1 R2 R1 R0 0 FB NF1 NF0   LSB

  R2, R1 and R0 must be 1, 1 and 0. If FB is 0, periodic noise
  is generated. If FB is 1, white noise is produced.

  NF1 and NF2 control the shift rate of the noise generator:

  NF1     NF2     Shift Rate
  --------------------------
  0       0       M/64
  0       1       M/128
  1       0       M/256
  1       1       Uses tone generator 3 output


  M is related to the clock rate.

  The voice start routines return 0 if all is well, -1 if we had
  trouble accessing the device file for the beeper and -2 if given
  parameters were out of range:
*/

#include <fcntl.h>
#include <sys/hilioctl.h>

/*********************************************************************
*DEFINES:                                                            *
*********************************************************************/

#define ALL_OK           0
#define ACCESS_PROBLEM  -1
#define BAD_RANGE       -2
#define BEEPER_DEVICE   "/dev/rhil"

#define VOICE1_FREQ_REG 0x80          /* Top nibbles for byte0 for all voices: */
#define VOICE2_FREQ_REG 0xA0
#define VOICE3_FREQ_REG 0xC0
#define NOISE_FREQ_REG  0xE0

#define VOICE1_VOL_REG  0x90          /* Top nibbles for byte2 for all voices: */
#define VOICE2_VOL_REG  0xB0
#define VOICE3_VOL_REG  0xD0
#define NOISE_VOL_REG   0xF0

#define MIN_VOICE       1             /* Legal ranges for parms from user: */
#define MAX_VOICE       3
#define MIN_PITCH       0
#define MAX_PITCH       1023
#define MIN_DURATION    0
#define MAX_DURATION    255
#define MIN_VOLUME      0
#define MAX_VOLUME      15
#define MIN_TYPE        0
#define MAX_TYPE        1
#define MIN_RATE        0
#define MAX_RATE        3

static int beeper_fd = -1;

/*********************************************************************
* PUBLIC FUNCTIONS:                                                  *
*********************************************************************/

/*****************************************************************************
*                                                                            *
* Beep using specified voice:                                                *
*                                                                            *
*   TAKES:                                                                   *
*                                                                            *
*   VOICE    : from 1 to 3                                                   *
*   PITCH    : from 0 to 1023 (incl)                                         *
*   VOLUME   : from 0 to 15   (incl). Zero turns voice off.                  *
*   DURATION : from 0 to 255  (incl). Zero turns voice on continuously.      *
*                                                                            *
*   RETURNS:                                                                 *
*                                                                            *
*   0        : All ok                                                        *
*   -1       : Cannot access beeper device file                              *
*   -2       : Parameter out of range                                        *
*                                                                            *
******************************************************************************/

beep(voice,pitch,volume,duration)

int voice,pitch,volume,duration;

{

unsigned char buffer[4];

  /* Check ranges of parameters: */
  if (
       (voice < MIN_VOICE)       ||
       (voice > MAX_VOICE)       ||
       (pitch < MIN_PITCH)       ||
       (pitch > MAX_PITCH)       ||
       (volume < MIN_VOLUME)     ||
       (volume > MAX_VOLUME)     ||
       (duration < MIN_DURATION) ||
       (duration > MAX_DURATION)
     )
    return(BAD_RANGE);
  /* Check whether beeper device has already been opened: */
  if (beeper_fd < 0)
    if ((beeper_fd = open(BEEPER_DEVICE,O_RDWR)) < 0)
      return(ACCESS_PROBLEM);
  /* Init the voice dependent data bytes. Note the inversion of user's
     volume and pitch specs to attenuation:
  */
  volume = MAX_VOLUME - volume;
  pitch  = MAX_PITCH  - pitch;
  switch (voice)
  {
    case 1: buffer[0] = VOICE1_FREQ_REG | (pitch & 0x0000000f);
            buffer[2] = VOICE1_VOL_REG  | (volume & 0x0000000f);
            break;
    case 2: buffer[0] = VOICE2_FREQ_REG | (pitch & 0x0000000f);
            buffer[2] = VOICE2_VOL_REG  | (volume & 0x0000000f);
            break;
    case 3: buffer[0] = VOICE3_FREQ_REG | (pitch & 0x0000000f);
            buffer[2] = VOICE3_VOL_REG  | (volume & 0x0000000f);
            break;
  };
  /* The high 6 bits of the pitch go into byte 1: */
  buffer[1] = 0x0000003f & (pitch >> 4);
  /* Duration: */
  buffer[3] = duration;
  if (ioctl(beeper_fd,EFTSBP,buffer) < 0)
    return(ACCESS_PROBLEM);
  return(ALL_OK);
} /* end function beep */
/*----------------------------------------------------*/

/******************************************************************************
* Produce noise.                                                              *
*                                                                             *
*   TAKES:                                                                    *
*                                                                             *
*   TYPE     : from 0 to 1.   0 is periodic noise. 1 is white noise.          *
*   RATE     : from 0 to 3.   0 is M/64. 1 is M/128. 2 is M/256. 3 means rate *
*                             determined by output of voice 3.                *
*   VOLUME   : from 0 to 15   (incl). Zero turns voice off.                   *
*   DURATION : from 0 to 255  (incl). Zero turns voice on continuously.       *
*                                                                             *
*   RETURNS:                                                                  *
*                                                                             *
*   0        : All ok                                                         *
*   -1       : Cannot access beeper device file                               *
*   -2       : Parameter out of range                                         *
******************************************************************************/

noise(type,rate,volume,duration)

int type,rate,volume,duration;

{

unsigned char buffer[4];

  /* Check ranges of parameters: */
  if (
       (type < MIN_TYPE)         || 
       (type > MAX_TYPE)         ||
       (rate < MIN_RATE)         ||
       (rate > MAX_RATE)         ||
       (volume < MIN_VOLUME)     ||
       (volume > MAX_VOLUME)     ||
       (duration < MIN_DURATION) ||
       (duration > MAX_DURATION)
     )
    return(BAD_RANGE);
  /* Check whether beeper device has already been opened: */
  if (beeper_fd < 0)
    if ((beeper_fd = open(BEEPER_DEVICE,O_RDWR)) < 0)
      return(ACCESS_PROBLEM);
  /* Invert the volume provided by the user getting the attenuation: */
  volume = MAX_VOLUME - volume;
  type   = type << 2;
  buffer[0] = NOISE_FREQ_REG | (type | rate);
  /* Byte one must be identical to byte 0: */
  buffer[1] = buffer[0];
  buffer[2] = NOISE_VOL_REG  | (volume & 0x0000000f);
  buffer[3] = duration;
  if (ioctl(beeper_fd,EFTSBP,buffer) < 0)
    return(ACCESS_PROBLEM);
  return(ALL_OK);
} /* end function noise */
/*----------------------------------------------------*/

/****************************************************************************
*                                                                           *
* Read how many 10msec intervalls are left till a voice has                 *
*   beeped to the end. This routine is good for noise also.                 *
*                                                                           *
*   TAKES:                                                                  *
*                                                                           *
*   voice    : from 1 to 4                                                  *
*                                                                           *
*   RETURNS:                                                                *
*                                                                           *
*   0        : Beeper has finished                                          *
*   >0       : Number of 10msec intervalls                                  *
*   -1       : Cannot access beeper device file                             *
*   -2       : Parameter out of range                                       *
*****************************************************************************/

beep_or_noise_left(voice)

int voice;

{

unsigned char buffer[4];

  /* Check for legal parameter range. The '+1' is to include noise generator: */
  if (
       (voice < MIN_VOICE)           ||
       (voice > (MAX_VOICE + 1))
     )
    return(BAD_RANGE);
  /* Check whether beeper device has already been opened: */
  if (beeper_fd < 0)
    if ((beeper_fd = open(BEEPER_DEVICE,O_RDWR)) < 0)
      return(ACCESS_PROBLEM);
  /* Get the timer values into the buffer bytes: */
  if (ioctl(beeper_fd,EFTRT,buffer) < 0)
    return(ACCESS_PROBLEM);
  return((int) buffer[voice-1]);
} /* end function beep_or_noise_left */
/*---------------------------------------------------*/
@//E*O*F beeper.c//
chmod u=rw,g=rw,o=r beeper.c
 
echo x - mytune.h
sed 's/^@//' > "mytune.h" <<'@//E*O*F mytune.h//'
int notes0[] =
  {5,9,0,5,9,7,5,2,11,0,-1,5,9,0,5,9,7,5,2,-42};
int octaves0[] =
  {2,2,3,3,3,3,3,3,2,3,0,2,2,3,3,3,3,3,3,-42};
int durations0[] =
  {187,63,125,125,125,125,125,125,125,125,750,187,63,125,125,125,125,125,125,-42};
struct voice_def voice0 = { notes0, octaves0, durations0};
int notes1[] =
  {0,8,9,0,8,9,0,8,9,0,0,8,9,0,8,9,0,8,9,-42};
int octaves1[] =
  {1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,-42};
int durations1[] =
  {250,125,125,250,125,125,250,125,125,125,125,125,125,250,125,125,250,125,125,-42};
struct voice_def voice1 = { notes1, octaves1, durations1};
int notes2[] =
  {5,8,9,5,8,9,5,8,9,5,5,8,9,5,8,9,5,8,9,-42};
int octaves2[] =
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-42};
int durations2[] =
  {250,125,125,250,125,125,250,125,125,125,125,125,125,250,125,125,250,125,125,-42};
struct voice_def voice2 = { notes2, octaves2, durations2};
struct stanza_def stanza1 = { &voice0, &voice1, &voice2, NULL};
int notes3[] =
  {0,2,8,2,8,5,-1,-42};
int octaves3[] =
  {3,3,2,3,2,2,0,-42};
int durations3[] =
  {750,125,1000,125,125,125,750,-42};
struct voice_def voice3 = { notes3, octaves3, durations3};
int notes4[] =
  {0,0,8,9,0,1,2,5,5,1,2,5,5,1,2,5,5,1,2,5,5,1,2,-42};
int octaves4[] =
  {1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-42};
int durations4[] =
  {125,125,125,125,250,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,-42};
struct voice_def voice4 = { notes4, octaves4, durations4};
int notes5[] =
  {5,5,8,9,5,1,2,10,10,1,2,10,10,1,2,10,10,1,2,10,10,1,2,-42};
int octaves5[] =
  {0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,-42};
int durations5[] =
  {125,125,125,125,250,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,-42};
struct voice_def voice5 = { notes5, octaves5, durations5};
struct stanza_def stanza2 = { &voice3, &voice4, &voice5, NULL};
int notes6[] =
  {5,9,0,5,9,7,5,2,11,0,-1,-1,0,7,7,4,0,-1,0,-42};
int octaves6[] =
  {2,2,3,3,3,3,3,3,2,3,0,0,3,3,3,3,3,0,3,-42};
int durations6[] =
  {187,63,125,125,125,125,125,125,125,125,500,125,125,250,250,125,125,125,125,-42};
struct voice_def voice6 = { notes6, octaves6, durations6};
int notes7[] =
  {0,8,9,0,8,9,0,8,9,0,0,3,4,7,7,3,4,7,7,3,4,-42};
int octaves7[] =
  {1,0,0,1,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,-42};
int durations7[] =
  {250,125,125,250,125,125,250,125,125,125,125,125,125,125,125,125,125,125,125,125,125,-42};
struct voice_def voice7 = { notes7, octaves7, durations7};
int notes8[] =
  {5,8,9,5,8,9,5,8,9,5,5,3,4,0,0,3,4,0,0,3,4,-42};
int octaves8[] =
  {0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,-42};
int durations8[] =
  {250,125,125,250,125,125,250,125,125,125,125,125,125,125,125,125,125,125,125,125,125,-42};
struct voice_def voice8 = { notes8, octaves8, durations8};
struct stanza_def stanza3 = { &voice6, &voice7, &voice8, NULL};
int notes9[] =
  {5,5,3,5,3,0,10,8,11,11,10,11,10,8,5,0,3,5,-42};
int octaves9[] =
  {3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,-42};
int durations9[] =
  {250,250,62,63,62,63,125,125,250,250,62,63,62,63,125,125,125,125,-42};
struct voice_def voice9 = { notes9, octaves9, durations9};
int notes10[] =
  {5,5,1,2,5,0,0,8,9,0,0,-42};
int octaves10[] =
  {1,1,1,1,1,1,1,0,0,1,1,-42};
int durations10[] =
  {125,125,125,125,500,125,125,125,125,500,250,-42};
struct voice_def voice10 = { notes10, octaves10, durations10};
int notes11[] =
  {10,10,1,2,10,5,5,8,9,5,5,-42};
int octaves11[] =
  {0,0,1,1,0,0,0,0,0,0,0,-42};
int durations11[] =
  {125,125,125,125,500,125,125,125,125,500,250,-42};
struct voice_def voice11 = { notes11, octaves11, durations11};
struct stanza_def stanza4 = { &voice9, &voice10, &voice11, NULL};
@//E*O*F mytune.h//
chmod u=rw,g=rw,o=r mytune.h
 
exit 0

jules@zen.UUCP (Julian Perry) (04/26/87)

In article <1365@ucbcad.berkeley.edu> chris@ic (Chris Guthrie) writes:
>We spent last night playing with the tone generator on our 320s.
>Although we were able to get a number of unique and interesting
>noises out of them, we couldn't figure out the layout of the
>four bytes in the EFTSBP ioctl command (other than the volume and
>timer bits).  Can anyone post the layout of these bytes.  Thanks!
>
>(The EFTSBP is the ioctl command for the rhil device which sends
>four data bytes to the tone generator.  Beyond that, none of my
>documentation details the purpose of those bytes).
>	Chris Guthrie
>	U.C.B.


Here is a small program  which  demonstates  how to use the beeper on an
hp9000s500, I guess it's the same on a 300.

The main  limitation of the  beeper  system  is the  poor  selection  of
frequencies  available  which  limits  its  tunefullness,  but  having 4
channels is really nice.

On a 500 you will need this device  file, as for a 300 I'll let you know
when I get one (along with the 850 etc etc)

crw-rw-rw- 1 root other 41 0xff0000 Apr 7 1986 /dev/hil8042


-----beep.c-----

#include <sys/hilioctl.h>

#define tone 13 
int ton[tone]={189,247,311,330,370,440,450,470,490,510,530,550,570};

int fd;

void
beep(voice,fre,amp,dur)

int    voice;
double fre;
int    amp;
double dur;

{

  struct eft2 bytes;
  int n;

  n = 1.2 * (83333.3 / fre);
  bytes.b[0] = (128 + (32 * voice) + (n % 16));
  bytes.b[1] = n / 16;
  bytes.b[2] = (128 + (32 * voice) + 31 - amp);     
  bytes.b[3] = (unsigned char) (dur * 100.0);
  ioctl(fd,EFTSBP,&bytes);

}

main()
{
  int b=0;
  double root, third, fifth;
  int i,lp;

  fd = open("/dev/hil8042",0);
  for ( lp = 0 ; lp < tone ; ++lp )
  {
    root = ton[lp];
    third = root * 1.26;
    fifth = root * 1.5;
    beep(0,root,15,0.9);
    beep(1,third,15,0.9);
    beep(2,fifth,15,0.9);
    for ( i = 0 ; i < 50000 ; ++i )
      i = i+0;
  }
  for ( lp = tone - 1 ; lp >= 0 ; --lp )
  {
    root = ton[lp];
    third = root * 1.26;
    fifth = root * 1.5;
    beep(0,root,15,0.9);
    beep(1,third,15,0.9);
    beep(2,fifth,15,0.9);
    for ( i = 0 ; i < 50000 ; ++i )
      i = i+0;
  }
  close(fd);

}

--------------

Jules [the noisy]

-- 
IN-REAL-LIFE:  Julian Perry           
E-MAIL:        jules@zen.co.uk || ...!mcvax!ukc!zen.co.uk!jules
PHONE:         +44 532 489048 ext 217
ADDRESS:       Zengrange Limited, Greenfield Road, Leeds, England, LS9 8DB

bayes@hpfcrj.HP.COM (Scott Bayes) (04/29/87)

As far as I can see, Tom Lane's exposition of the H/W guts of the 4-voice
beeper is correct, at least as far as the hardware is concerned. I should
know, I have to maintain the document he mentioned, and that part hasn't
changed.

As far as talking with this device using HP-UX is concerned, check out
Peter Marvit's response. Putting the two responses together should give you 
a pretty good model right down to the silicon.

Scott Bayes
hpfcla!bayes