{===========================================================================
Date: 08-31-93 (22:24)
From: WIM VAN.VOLLENHOVEN
Subj: Sound Module
---------------------------------------------------------------------------
Well.. here is the source code i've found in a pascal toolbox (ECO)
which emulates the play function of qbasic :-)

{
  call: play(string)

        music_string --- the string containing the encoded music to be
                         played.  the format is the same as that of the
                         microsoft basic play statement.  the string
                         must be <= 254 characters in length.

  calls:  sound
          getint  (internal)

  remarks:  the characters accepted by this routine are:

            a - g       musical notes
            # or +      following a - g note, indicates sharp
            -           following a - g note, indicates flat
            <           move down one octave
            >           move up one octave
            .           dot previous note (extend note duration by 3/2)
            mn          normal duration (7/8 of interval between notes)
            ms          staccato duration
            ml          legato duration
            ln          length of note (n=1-64; 1=whole note,4=quarter note)
            pn          pause length (same n values as ln above)
            tn          tempo,n=notes/minute (n=32-255,default n=120)
            on          octave number (n=0-6,default n=4)
            nn          play note number n (n=0-84)

            the following two commands are ignored by play:

            mf          complete note before continuing
            mb          another process may begin before speaker is
                        finished playing note

  important --- setdefaultnotes must have been called at least once before
                this routine is called.
}

unit u_play;
interface

uses
  crt

  ;

const
  note_octave   : integer = 4;     { current octave for note            }
  note_fraction : real    = 0.875; { fraction of duration given to note }
  note_duration : integer = 0;     { duration of note     ^^semi-legato }
  note_length   : real    = 0.25;  { length of note }
  note_quarter  : real    = 500.0; { moderato pace (principal beat)     }



  procedure quitsound;
  procedure startsound;
  procedure errorbeep;
  procedure warningbeep;
  procedure smallbeep;
  procedure setdefaultnotes;
  procedure play(s: string);
  procedure beep(h, l: word);



implementation




  procedure quitsound;
  var i: word;
  begin
    for i := 100 downto 1 do begin sound(i*10); delay(2) end;
    for i := 1 to 800 do begin sound(i*10); delay(2) end;
    nosound;
  end;

  procedure startsound;
  var i: word;
  begin
    for i := 100 downto 1 do begin sound(i*15); delay(2) end;
    for i := 1 to 100 do begin sound(i*15); delay(2) end; nosound;
    delay(100); for i := 100 downto 1 do begin sound(i*10); delay(2) end;
    nosound;
  end;


  procedure errorbeep;
  begin
    sound(2000); delay(75); sound(1000); delay(75); nosound;
  end;


  procedure warningbeep;
  begin
    sound(500); delay(500); nosound;
  end;

  procedure smallbeep;
  begin
    sound(300); delay(50); nosound;
  end;





procedure setdefaultnotes;
begin
   note_octave   := 4;             { default octave                      }
   note_fraction := 0.875;         { default sustain is semi-legato      }
   note_length   := 0.25;          { note is quarter note by default     }
   note_quarter  := 500.0;         { moderato pace by default            }
end;



procedure play(s: string);
const
                                      { offsets in octave of natural notes }
 note_offset   : array[ 'A'..'G' ] of integer = (9,11,0,2,4,5,7);

                                      { frequencies for 7 octaves          }
   note_freqs: array[ 0 .. 84 ] of integer =
{
      c    c#     d    d#     e     f    f#     g    g#     a    a#     b
}
(    0,
     65,  69,  73,  78,  82,  87,  92,  98, 104, 110, 116, 123,
    131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
    262, 278, 294, 312, 330, 350, 370, 392, 416, 440, 466, 494,
    524, 556, 588, 624, 660, 700, 740, 784, 832, 880, 932, 988,
   1048,1112,1176,1248,1320,1400,1480,1568,1664,1760,1864,1976,
   2096,2224,2352,2496,2640,2800,2960,3136,3328,3520,3728,3952,
   4192,4448,4704,4992,5280,5600,5920,6272,6656,7040,7456,7904 );

   quarter_note = 0.25;            { length of a quarter note }

   digits : set of '0'..'9' = ['0'..'9'];

var

   play_freq     : integer;        { frequency of note to be played }
   play_duration : integer;        { duration to sound note }
   rest_duration : integer;        { duration of rest after a note }
   i             : integer;        { offset in music string }
   c             : char;           { current character in music string }
                                   { note frequencies }
   freq          : array[0..6,0..11] of integer absolute note_freqs;
   n             : integer;
   xn            : real;
   k             : integer;

  function getint : integer;
  var n: integer;

  begin { getint }
    n := 0;
    while(s[i] in digits) do begin n := n*10+ord(s[i])-ord('0'); inc(i) end;
    dec(i); getint := n;
  end   { getint };

begin
  s := s + ' ';                   { append blank to end of music string }
  i := 1;                           { point to first character in music }
  while(i < length(s)) do begin      { begin loop over music string }
    c := upcase(s[i]);        { get next character in music string }
    case c of                 { interpret it                       }
       'A'..'G' : begin { a note }
          n         := note_offset[ c ];
          play_freq := freq[ note_octave ,n ];
          xn := note_quarter * (note_length / quarter_note);
          play_duration := trunc(xn * note_fraction);
          rest_duration := trunc(xn * (1.0 - note_fraction));
                                      { check for sharp/flat }
          if s[i+1] in ['#','+','-' ] then
             begin
                inc(i);
                case s[i] of
                   '#',
                   '+' : play_freq :=
                            freq[ note_octave ,succ(n) ];
                   '-' : play_freq :=
                            freq[ note_octave ,pred(n) ];
                   else  ;
                end { case };

             end;

                   { check for note length }

          if (s[i+1] in digits) then
             begin

                inc(i);
                n  := getint;
                xn := (1.0 / n) / quarter_note;

                play_duration :=
                    trunc(note_fraction * note_quarter * xn);

                rest_duration :=
                   trunc((1.0 - note_fraction) *
                          xn * note_quarter);

             end;
                   { check for dotting }

             if s[i+1] = '.' then
                begin

                   xn := 1.0;

                   while(s[i+1] = '.') do
                      begin
                         xn := xn * 1.5;
                         inc(i);
                      end;

                   play_duration :=
                       trunc(play_duration * xn);

                end;

                       { play the note }

          sound(play_freq);
          delay(play_duration);
          nosound;
          delay(rest_duration);
        end   { a note };

       'M' : begin { 'M' commands }
         inc(i);
         c := s[i];
         case c of
           'F' : ;
           'B' : ;
           'N' : note_fraction := 0.875;
           'L' : note_fraction := 1.000;
           'S' : note_fraction := 0.750;
           else ;
         end { case };
       end   { 'M' commands };

       'O' : begin { set octave }
         inc(i);
         n := ord(s[i]) - ord('0');
         if (n < 0) or (n > 6) then n := 4;
         note_octave := n;
       end   { set octave };

       '<' : begin { drop an octave }
         if note_octave > 0 then dec(note_octave);
       end   { drop an octave };

       '>' : begin { ascend an octave }
         if note_octave < 6 then inc(note_octave);
       end   { ascend an octave };

       'N' : begin { play note n }
         inc(i); n := getint;
         if (n > 0) and (n <= 84) then begin
           play_freq     := note_freqs[ n ];
           xn            := note_quarter * (note_length / quarter_note);
           play_duration := trunc(xn * note_fraction);
           rest_duration := trunc(xn * (1.0 - note_fraction));
         end else if (n = 0) then begin
           play_freq     := 0; play_duration := 0;
           rest_duration := trunc(note_fraction * note_quarter *
                                 (note_length / quarter_note));
         end;
         sound(play_freq); delay(play_duration); nosound;
         delay(rest_duration);
       end   { play note n };
       'L' : begin { set length of notes }
         inc(i); n := getint;
         if n > 0 then note_length := 1.0 / n;
       end   { set length of notes };

       'T' : begin { # of quarter notes in a minute }
         inc(i); n := getint;
         note_quarter := (1092.0 / 18.2 / n) * 1000.0;
       end   { # of quarter notes in a minute };

       'P' : begin { pause }
         inc(i); n := getint;
         if (n <  1) then n := 1 else if (n > 64) then n := 64;
         play_freq := 0; play_duration := 0;
         rest_duration := trunc(((1.0 / n) / quarter_note) * note_quarter);
         sound(play_freq); delay(play_duration); nosound;
         delay(rest_duration);
       end   { pause };

       else  { ignore other stuff };
    end { case };
    inc(i);
  end  { interpret music };
  nosound;                         { make sure sound turned off when through }
end;


procedure beep(h, l: word);
begin
  sound(h); delay(l); nosound;
end;

end. { of unit }
