REM
REM Implement a BEEP over the hardware PC speaker.
REM Currently Linux only - tested with Ubuntu 9.04
REM
REM (c) PvE - GPL - July 11, 2009.
REM Improved error handling - Oct 11, 2009.
REM Adapted for BaCon 3.0 and higher - Apr 22, 2014.
REM
REM ---------------------------------------------------------- Manual
REM
REM Include this file into your program with: INCLUDE "beep.bac"
REM
REM It implements two calls: 'BEEP' and 'SHUT'.
REM
REM 1. BEEP (frequency, time)
REM
REM Let the hardware PC speaker sound the frequency for a duration
REM of time.
REM
REM 2. SHUT
REM
REM Shutdown all sound pending on the hardware PC speaker.
REM
REM
REM See also 'piano.bac' for an example on usage of the BEEP INCLUDE.
REM
REM ---------------------------------------------------------- Frequencies
REM
REM TABLE OF MUSICAL NOTE FREQUENCIES (Hz)
REM
REM  Octave 0    1    2    3    4    5    6    7
REM Note
REM  C     16   33   65  131  262  523  1046  2093
REM  C#    17   35   69  139  277  554  1109  2217
REM  D     18   37   73  147  294  587  1175  2349
REM  D#    19   39   78  155  311  622  1244  2489
REM  E     21   41   82  165  330  659  1328  2637
REM  F     22   44   87  175  349  698  1397  2794
REM  F#    23   46   92  185  370  740  1480  2960
REM  G     24   49   98  196  392  784  1568  3136
REM  G#    26   52  104  208  415  831  1661  3322
REM  A     27   55  110  220  440  880  1760  3520
REM  A#    29   58  116  233  466  932  1865  3729
REM  B     31   62  123  245  494  988  1975  3951
REM
REM ---------------------------------------------------------- Initialize

REM Check on the availability of ioctl
TRAP LOCAL
CATCH GOTO beep_print_message

REM Make sure X reacts on bell
SYSTEM "xset b on 2>/dev/null"

REM Get the system call for console environment
IMPORT "ioctl" FROM "libc.so" TYPE int

REM Define values for console environment
DEF FN beep_KIOCSOUND = DEC("4B2F")
DEF FN beep_KDMKTONE = DEC("4B30")
CONST beep_DEVICE = 1
CONST beep_X86_oscillator = 1193180

REM Flag that we use X environment
GLOBAL beep_NoUseX = 0

REM Get the system calls for an X environment
CATCH GOTO beep_No_X_Environment
IMPORT "XOpenDisplay" FROM "libX11.so.6" TYPE long
IMPORT "XGetKeyboardControl" FROM "libX11.so.6" TYPE void
IMPORT "XChangeKeyboardControl" FROM "libX11.so.6" TYPE void
IMPORT "XCloseDisplay" FROM "libX11.so.6" TYPE void
IMPORT "XBell" FROM "libX11.so.6" TYPE void
IMPORT "XFlush" FROM "libX11.so.6" TYPE void

REM Define values for the X environment
CONST beep_KBBellPitch = 1 << 2
CONST beep_KBBellDuration = 1 << 3

RECORD beep_XKeyboardState
    LOCAL key_click_percent TYPE int
    LOCAL bell_percent TYPE int
    LOCAL bell_pitch, bell_duration TYPE unsigned int
    LOCAL led_mask TYPE unsigned long
    LOCAL global_auto_repeat TYPE int
    LOCAL auto_repeats[32] TYPE char
END RECORD

RECORD beep_XKeyboardControl
    LOCAL key_click_percent TYPE int
    LOCAL bell_percent TYPE int
    LOCAL bell_pitch TYPE int
    LOCAL bell_duration TYPE int
    LOCAL led TYPE int
    LOCAL led_mode TYPE int
    LOCAL key TYPE int
    LOCAL auto_repeat_mode TYPE int
END RECORD

REM ---------------------------------------------------------- Implementation

REM The BEEP function
SUB BEEP(NUMBER freq, NUMBER duration)

    LOCAL result, display

    IF freq > 0 THEN

        REM Run the beep on the console
        result = ioctl(beep_DEVICE, beep_KIOCSOUND, beep_X86_oscillator / freq)

        IF result < 0 THEN

            REM Console did not succeed, try X environment
            IF ISFALSE(beep_NoUseX) THEN
                display = XOpenDisplay(0)
                XGetKeyboardControl(display, ADDRESS(beep_XKeyboardState))
                beep_XKeyboardControl.key_click_percent = 0
                beep_XKeyboardControl.bell_percent = 0
                beep_XKeyboardControl.bell_pitch = freq
                beep_XKeyboardControl.bell_duration = duration
                beep_XKeyboardControl.led = 0
                beep_XKeyboardControl.led_mode = 0
                beep_XKeyboardControl.key = 0
                beep_XKeyboardControl.auto_repeat_mode = 0
                XChangeKeyboardControl(display, beep_KBBellDuration | beep_KBBellPitch, ADDRESS(beep_XKeyboardControl))
                XBell(display, 100)
                XFlush(display)
                SLEEP duration
                XChangeKeyboardControl(display, beep_KBBellDuration | beep_KBBellPitch, ADDRESS(beep_XKeyboardState))
                XCloseDisplay(display)
            ELSE
                PRINT "Cannot use PC speaker on this machine!"
                END IF
        ELSE
            REM Stop the beep on the console
            SLEEP duration*2
            ioctl(beep_DEVICE, beep_KIOCSOUND, 0)
        END IF
    END IF

END SUB

REM The SHUT function
SUB SHUT

    LOCAL result, display

    REM Stop the beep on the console
    result = ioctl(beep_DEVICE, beep_KIOCSOUND, 0)

    IF result < 0 THEN

        REM Console did not succeed, try X environment
        IF ISFALSE(beep_NoUseX) THEN
            display = XOpenDisplay(0)
            XFlush(display)
            XCloseDisplay(display)
        ELSE
            PRINT "Cannot use PC speaker on this machine!"
        END IF

    END IF

END SUB

REM Goto end of include file
GOTO beep_END

REM ---------------------------------------------------------- Error handling

REM Call 'ioctl' not found
LABEL beep_print_message
    PRINT "IOCTL is not available on this platform! Check if 'libc.so' exists."
    END

REM We will not use the X environment
LABEL beep_No_X_Environment
    beep_NoUseX = 1
    PRINT "No X"
    RESUME

LABEL beep_END
    TRAP SYSTEM
    CATCH RESET