You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
2 years ago
|
// +build linux
|
||
|
|
||
|
package staros
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"os"
|
||
|
"syscall"
|
||
|
"time"
|
||
|
"unsafe"
|
||
|
)
|
||
|
|
||
|
// Constants
|
||
|
const (
|
||
|
// This number represents the fixed frequency of the original PC XT's timer chip, which is approximately 1.193 MHz. This number
|
||
|
// is divided with the desired frequency to obtain a counter value, that is subsequently fed into the timer chip, tied to the PC speaker.
|
||
|
clockTickRate = 1193180
|
||
|
|
||
|
// linux/kd.h, start sound generation (0 for off)
|
||
|
kiocsound = 0x4B2F
|
||
|
|
||
|
// linux/input-event-codes.h
|
||
|
evSnd = 0x12 // Event type
|
||
|
sndTone = 0x02 // Sound
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// DefaultFreq - frequency, in Hz, middle A
|
||
|
DefaultFreq = 440.0
|
||
|
// DefaultDuration - duration in milliseconds
|
||
|
DefaultDuration = 200
|
||
|
)
|
||
|
|
||
|
// inputEvent represents linux/input.h event structure.
|
||
|
type inputEvent struct {
|
||
|
Time syscall.Timeval // time in seconds since epoch at which event occurred
|
||
|
Type uint16 // event type
|
||
|
Code uint16 // event code related to the event type
|
||
|
Value int32 // event value related to the event type
|
||
|
}
|
||
|
|
||
|
// ioctl system call manipulates the underlying device parameters of special files.
|
||
|
func ioctl(fd, name, data uintptr) error {
|
||
|
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, name, data)
|
||
|
if e != 0 {
|
||
|
return e
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker).
|
||
|
//
|
||
|
// On Linux it needs permission to access `/dev/tty0` or `/dev/input/by-path/platform-pcspkr-event-spkr` files for writing,
|
||
|
// and `pcspkr` module must be loaded. User must be in correct groups, usually `input` and/or `tty`.
|
||
|
//
|
||
|
// If it can not open device files, it will fallback to sending Bell character (https://en.wikipedia.org/wiki/Bell_character).
|
||
|
// For bell character in X11 terminals you can enable bell with `xset b on`. For console check `setterm` and `--blength` or `--bfreq` options.
|
||
|
//
|
||
|
// On macOS this just sends bell character. Enable `Audible bell` in Terminal --> Preferences --> Settings --> Advanced.
|
||
|
//
|
||
|
// On Windows it uses Beep function via syscall.
|
||
|
//
|
||
|
// On Web it plays hard coded beep sound.
|
||
|
func Beep(freq float64, duration int) error {
|
||
|
if freq == 0 {
|
||
|
freq = DefaultFreq
|
||
|
} else if freq > 20000 {
|
||
|
freq = 20000
|
||
|
} else if freq < 0 {
|
||
|
freq = DefaultFreq
|
||
|
}
|
||
|
|
||
|
if duration == 0 {
|
||
|
duration = DefaultDuration
|
||
|
}
|
||
|
|
||
|
period := int(float64(clockTickRate) / freq)
|
||
|
|
||
|
var evdev bool
|
||
|
|
||
|
f, err := os.OpenFile("/dev/tty0", os.O_WRONLY, 0644)
|
||
|
if err != nil {
|
||
|
e := err
|
||
|
f, err = os.OpenFile("/dev/input/by-path/platform-pcspkr-event-spkr", os.O_WRONLY, 0644)
|
||
|
if err != nil {
|
||
|
e = errors.New("beeep: " + e.Error() + "; " + err.Error())
|
||
|
|
||
|
// Output the only beep we can
|
||
|
_, err = os.Stdout.Write([]byte{7})
|
||
|
if err != nil {
|
||
|
return errors.New(e.Error() + "; " + err.Error())
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
evdev = true
|
||
|
}
|
||
|
|
||
|
defer f.Close()
|
||
|
|
||
|
if evdev { // Use Linux evdev API
|
||
|
ev := inputEvent{}
|
||
|
ev.Type = evSnd
|
||
|
ev.Code = sndTone
|
||
|
ev.Value = int32(freq)
|
||
|
|
||
|
d := *(*[unsafe.Sizeof(ev)]byte)(unsafe.Pointer(&ev))
|
||
|
|
||
|
// Start beep
|
||
|
f.Write(d[:])
|
||
|
|
||
|
time.Sleep(time.Duration(duration) * time.Millisecond)
|
||
|
|
||
|
ev.Value = 0
|
||
|
d = *(*[unsafe.Sizeof(ev)]byte)(unsafe.Pointer(&ev))
|
||
|
|
||
|
// Stop beep
|
||
|
f.Write(d[:])
|
||
|
} else { // Use ioctl
|
||
|
// Start beep
|
||
|
err = ioctl(f.Fd(), kiocsound, uintptr(period))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
time.Sleep(time.Duration(duration) * time.Millisecond)
|
||
|
|
||
|
// Stop beep
|
||
|
err = ioctl(f.Fd(), kiocsound, uintptr(0))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|