Oberon/ETH Oberon/speaker

This document was originally hosted at the ETHZ. It remains under the ETH license and is in the WayBack archive.

Back to: Native Oberon Hardware Compatiblity List

 
Speaker support
 
Summary

The PC speaker is a poor man's sound generator capable of no more than producing pitiful beeps and squawks, but it is simple to program in Oberon. However, since the SYSTEM module is used, code is not portable.

How to control the speaker by program

Assume that a sound of a frequency f should be emitted during a time t. The system Timer 2, one of three timers in the Programmable Interval Timer (PIT), is used to control sound generation, in five sucessive steps. The PIT operates at 1.19318 MHz.

  • compute a 2-byte counter value (1 <= count <= 65'535) corresponding to the specified frequency f - IntFreq. 1'193'180 >= f >= 18.2
  • send a PIT control word to port address 43H - InitTimer
  • send the counter value to port address 42H - SetFreq
  • set "on" the bits 0 and 1 of the control register at port address 61H to switch on the speaker and the amplifier - Tone
  • after a time delay t, set "off" the same bits at port address 61H - Tone

The time delay must be controlled by a background task. Note that once the speaker is "on", the frequency may be changed at will, even set to something inaudible to simulate a silence.

Program snippets
CONST 
   TONES = 12;  OCTAVES = 10; 
   ON = TRUE;   OFF = FALSE; 
   A4 = 440;  (* the latin "la" *) 


(* Convert the frequency to an "internal" value *) 
PROCEDURE IntFreq(hz: LONGINT): INTEGER; 
CONST Rate  = 1193180;   (* Timer clock is 1.19318 MHz *) 
BEGIN 
   RETURN SHORT(Rate DIV hz) 
END IntFreq; 


(* Set PIT control word to: Timer 2, 16-bit, mode 3, binary = {01110110} i.e. B6H *) 
PROCEDURE InitTimer;   (* Timer port address: 43H *) 
BEGIN 
  SYSTEM.PORTOUT (43H, 0B6H) 
END InitTimer; 


(* Set counter value *) 
PROCEDURE SetFreq(hz: LONGINT);  (* Timer 2 port address: 42H *) 
BEGIN 
   SYSTEM.PORTOUT (42H, CHR(hz MOD 100H)); 
   SYSTEM.PORTOUT (42H, CHR(hz DIV 100H)); 
   Tone(ON) 
END SetFreq; 


(* Switch speaker and amplifier on/off *) 
PROCEDURE Tone(b: BOOLEAN);  (* Loudspeaker port address: 61H *) 
VAR s: SET; 
BEGIN 
   SYSTEM.PORTIN (61H, SYSTEM.VAL(CHAR, s)); 
   IF b THEN s := s + {0, 1} ELSE s := s - {0, 1} END; 
   SYSTEM.PORTOUT (61H, SYSTEM.VAL(CHAR, s)) 
END Tone; 


(* Calculate table of "internal" frequencies for a number of octaves *) 
PROCEDURE CalcTF; 
VAR t, oct: INTEGER; f, f0, df: REAL; 
BEGIN 
   df := Math.exp(Math.ln(2) / TONES); 
   f0 := A4 * df * df * df / 32;   (* = c0 *) 
   FOR oct := 0 TO OCTAVES-1 DO 
      f := f0; 
      FOR t := 0 TO TONES-1 DO 
         TF[t, oct] := IntFreq(ENTIER(f)); 
         f := f * df 
      END; 
      f0 := f0 * 2 
   END 
END CalcTF; 


(* Beep on the speaker: beepFreq during beepLen *) 
PROCEDURE Beep*; 
VAR wakeUp: LONGINT; 
BEGIN 
   IF ~timerActive THEN 
      InitTimer; 
      SetFreq(beepFreq); 
      wakeUp := Input.Time() + beepLen * (Input.TimeUnit DIV 1000); 
      REPEAT UNTIL Input.Time() - wakeUp >= 0; 
      Tone(OFF); 
      timerActive := FALSE 
   END 
END Beep;

13 Jul 2002 - Copyright © 2002 ETH Zürich. All rights reserved.
E-Mail: oberon at lists.inf.ethz.ch
Homepage: http://www.ethoberon.ethz.ch/