Back to: Native Oberon Hardware Compatiblity List

Speaker support


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.19318MHz.

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

   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-web at