Control PC Speaker volume or change program doing the beep?

xfce4-terminal does a loud beep, but the beep command does a quieter one.

Is there a way to adjust the volume from the beep Terminal does? Or perhaps change Terminal's beep to use /usr/bin/beep?


I just realized beep does the beep through my regular speakers, and guess that explains the volume difference :p

But I wonder if the PC speaker on the motherboard could still be volume adjusted? And if Xfce terminal could use /usr/bin/beep instead of the PC speaker?
 
As already said, the PC speaker's volume cannot be adjusted (it's simply just what the bell from typewriters were.) But it (shall) only beep when something was wrong very basically (mainboard, hardware level.)
You better check for that (the beeping [one short, or long time, one or three beeps] may also tell you more about, what's wrong -> mainboard's/BIOS man.)
Or you need to find somewhere you set wrong in your operating system, desktop environment, software,... maybe you once played somewhere with the PC speaker, and forgot about it.

Anyway, the only way to adjust PC speaker's volume I see is: Look for a plug you can pull, or a wire to cut to mute it completely (Warning! All hardware stunts on your mainboard are completely at your own risk! Only do, what you are certain what you're doing, why, and how!), add a resistor into the speaker's cable (while todays mainboards mostly have speakers directly soldered on, so no wires [Don't you even think of touching the conducting lanes! Unless you are really a versed electronics technician with the proper equipment, and the knowledge and experience!],
or simply tape damping material (Not conducting!) over it.
But that was no good idea anyway, since today PC speakers are mostly for telling the user something's wrong with the hardware, and otherwise be mute.
 
xset might be able to adjust PC speaker volume (man page mentions volume with b flag), but for me it seems to adjust pitch or something not related Bell volume might reference the actual Terminal bell volume but not PC speaker?

Doing this changed the Terminal beep tone to be less high-pitched:

Code:
xset b 100 400 20
 
i grepped a bit and found that X will use your console bell if not told otherwise which uses the pc speaker
it seems apps may register their own bells too
afaik (in xt times) the speaker was connected to a programable timer/oscilator and had no other settings like volume, decay whatever
it seems that pulse audio and pipewire can somehow hook into X events and override the system speaker with a wav file that will be played thru the sound server
the idea is to add an X client which monitors all of the display bell events and plays a sound via oss instead of letting X use they ttyvX bell
 
I remember way back in the DOS days when someone wrote machine code to synthesize the human voice over the PC speaker. It was that code that got me interested in the concepts of pcm (pulse-code modulation) and pwm (pulse width modulation). The really really bad voice reproduction was due to the limiting factor of the available clock speed to pulse the speaker. So, the "trick" to lowering the speaker volume is to use a low duty cycle pwm signal...but again, the clock driving the speaker isn't really fast enough to do much except the standard error beeps.
 
afaik (in xt times) the speaker was connected to a programable timer/oscilator and had no other settings like volume, decay whatever
I get different pitches if I change sysctl machdep.i8254_freq

Would specifying something there be ok? It sounds like that might affect things other than a speaker? https://en.wikipedia.org/wiki/Intel_8253#IBM_PC_programming mentions:

Newer motherboards include additional counters through the Advanced Configuration and Power Interface (ACPI), a counter on the Local Advanced Programmable Interrupt Controller, and a High Precision Event Timer. The CPU itself also provides the Time Stamp Counter facility.

So it sounds like if I'm using TSC or HPET, changing the i8254 frequency shouldn't affect timers or performance elsewhere?
 
this does what you want
build with
cc -I /usr/local/include/ -L /usr/local/lib -lX11 -lpthread -lm y.c -o y
run ./y in another term
the beep code is taken from beep.c (/usr/bin/beep) and the programs accepts the same args as beep(1) (except background run)
the X part is AI generated but it works (code is similar with those of pulse audion x11-bell-mod
i tested it an it works
C:
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <math.h>
#include <paths.h>
#include <stdbool.h>

#define LOG_FILE "/tmp/xbell_events.log"

#define SAMPLE_RATE_DEF 48000           /* hz */
#define SAMPLE_RATE_MAX 48000           /* hz */
#define SAMPLE_RATE_MIN 8000            /* hz */

#define DURATION_DEF 150                /* ms */
#define DURATION_MAX 2000               /* ms */
#define DURATION_MIN 50                 /* ms */

#define GAIN_DEF 75
#define GAIN_MAX 100
#define GAIN_MIN 0

#define WAVE_POWER 1.25f

#define DEFAULT_HZ 440

#define DEFAULT_DEVICE _PATH_DEV "dsp"

static int frequency = DEFAULT_HZ;
static int duration_ms = DURATION_DEF;
static int sample_rate = SAMPLE_RATE_DEF;
static int gain = GAIN_DEF;
static const char *oss_dev = DEFAULT_DEVICE;
static bool background;
int f, c;
float *buffer;
size_t size;
/*
 * wave_function_16
 *
 * "phase" should be in the range [0.0f .. 1.0f>
 * "power" should be in the range <0.0f .. 2.0f>
 *
 * The return value is in the range [-1.0f .. 1.0f]
 */
static float
wave_function_16(float phase, float power)
{
        uint16_t x = phase * (1U << 16);
        float retval;
        uint8_t num;

        /* Handle special cases, if any */
        switch (x) {
        case 0xffff:
        case 0x0000:
                return (1.0f);
        case 0x3fff:
        case 0x4000:
        case 0xBfff:
        case 0xC000:
                return (0.0f);
        case 0x7FFF:
        case 0x8000:
                return (-1.0f);
        default:
                break;
        }

        /* Apply Gray coding */
        for (uint16_t mask = 1U << 15; mask != 1; mask /= 2) {
                if (x & mask)
                        x ^= (mask - 1);
        }

        /* Find first set bit */
        for (num = 0; num != 14; num++) {
                if (x & (1U << num)) {
                        num++;
                        break;
                }
        }

        /* Initialize return value */
        retval = 0.0;

        /* Compute the rest of the power series */
        for (; num != 14; num++) {
                if (x & (1U << num)) {
                        retval = (1.0f - retval) / 2.0f;
                        retval = powf(retval, power);
                } else {
                        retval = (1.0f + retval) / 2.0f;
                        retval = powf(retval, power);
                }
        }

        /* Check if halfway */
        if (x & (1ULL << 14))
                retval = -retval;

        return (retval);
}

static void
usage(void)
{
        fprintf(stderr, "Usage: %s [parameters]\n"
            "\t" "-F <frequency in HZ, default %d Hz>\n"
            "\t" "-D <duration in ms, from %d ms to %d ms, default %d ms>\n"
            "\t" "-r <sample rate in HZ, from %d Hz to %d Hz, default %d Hz>\n"
            "\t" "-d <OSS device (default %s)>\n"
            "\t" "-g <gain from %d to %d, default %d>\n"
            "\t" "-B Run in background\n"
            "\t" "-h Show usage\n",
            getprogname(),
            DEFAULT_HZ,
            DURATION_MIN, DURATION_MAX, DURATION_DEF,
            SAMPLE_RATE_MIN, SAMPLE_RATE_MAX, SAMPLE_RATE_DEF,
            DEFAULT_DEVICE,
            GAIN_MIN, GAIN_MAX, GAIN_DEF);
        exit(1);
}


/* Function to log bell events to file */
void log_bell_event(const char *message) {
    FILE *fp = fopen(LOG_FILE, "a");
    if (fp == NULL) {
        perror("Error opening log file");
        return;
    }

    time_t now = time(NULL);
    char *timestamp = ctime(&now);
    timestamp[strlen(timestamp) - 1] = '\0';

    fprintf(fp, "[%s] %s\n", timestamp, message);
    fclose(fp);
}

/* Play WAV file using OSS */
void *play_wav_thread(void *arg)
{
        if (write(f, buffer, size * sizeof(buffer[0])) !=
           (ssize_t)(size * sizeof(buffer[0])))
                errx(1, "failed writing to DSP device(%s)", oss_dev);

        /* wait for data to be written */
        while (ioctl(f, SNDCTL_DSP_GETODELAY, &c) == 0) {
                if (c == 0)
                        break;
                usleep(10000);
        }

        /* wait for audio to go out */
        usleep(50000);
return NULL;
}

/* Play WAV file in a separate thread */
void play_wav_file() {
    pthread_t thread;

    if (pthread_create(&thread, NULL, play_wav_thread, NULL) != 0) {
        log_bell_event("Error: Cannot create playback thread");
        return;
    }

    pthread_detach(thread);
}

/* XKB Bell event handler */
void handle_xkb_event(Display *dpy, XEvent *event) {
    XkbEvent *xkb_event = (XkbEvent *)event;

    if (xkb_event->any.xkb_type == XkbBellNotify) {
        XkbBellNotifyEvent *bell = (XkbBellNotifyEvent *)xkb_event;
        char msg[256];

        snprintf(msg, sizeof(msg),
                "Bell event detected - Window: 0x%lx, Percent: %d, Pitch: %d, Duration: %d - Playing WAV",
                bell->window, bell->percent, bell->pitch, bell->duration);

        log_bell_event(msg);
        printf("%s\n", msg);

        /* Play the WAV file */
        play_wav_file();
    }
}


int main(int argc, char *argv[]) {
    Display *dpy;
    int xkb_event_base, xkb_error_base;
    int major = XkbMajorVersion;
    int minor = XkbMinorVersion;
    XEvent event;
    size_t slope;
    size_t off;
    float a;
    float d;
    float p;

    /* Open connection to X server */
    dpy = XOpenDisplay(NULL);
    if (dpy == NULL) {
        fprintf(stderr, "Cannot open display\n");
        exit(1);
    }


        while ((c = getopt(argc, argv, "BF:D:r:g:d:h")) != -1) {
                switch (c) {
                case 'F':
                        frequency = strtol(optarg, NULL, 10);
                        break;
                case 'D':
                        duration_ms = strtol(optarg, NULL, 10);
                        if (duration_ms < DURATION_MIN ||
                            duration_ms > DURATION_MAX)
                                usage();
                        break;
                case 'r':
                        sample_rate = strtol(optarg, NULL, 10);
                        if (sample_rate < SAMPLE_RATE_MIN ||
                            sample_rate > SAMPLE_RATE_MAX)
                                usage();
                        break;
                case 'g':
                        gain = strtol(optarg, NULL, 10);
                        if (gain < GAIN_MIN ||
                            gain > GAIN_MAX)
                                usage();
                        break;
                case 'd':
                        oss_dev = optarg;
                        break;
                case 'B':
                        background = true;
                        break;
                default:
                        usage();
                        break;
                }
        }

        if (background && daemon(0, 0) != 0)
                errx(1, "daemon(0,0) failed");

        f = open(oss_dev, O_WRONLY);
        if (f < 0)
                err(1, "Failed to open '%s'", oss_dev);

        c = 1;                          /* mono */
        if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &c) != 0)
                errx(1, "ioctl SOUND_PCM_WRITE_CHANNELS(1) failed");

        c = AFMT_FLOAT;
        if (ioctl(f, SNDCTL_DSP_SETFMT, &c) != 0)
                errx(1, "ioctl SNDCTL_DSP_SETFMT(AFMT_FLOAT) failed");

        if (ioctl(f, SNDCTL_DSP_SPEED, &sample_rate) != 0)
                errx(1, "ioctl SNDCTL_DSP_SPEED(%d) failed", sample_rate);

        c = (2 << 16);
        while ((1ULL << (c & 63)) < (size_t)(4 * sample_rate / 50))
                c++;
        if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &c))
                errx(1, "ioctl SNDCTL_DSP_SETFRAGMENT(0x%x) failed", c);

        if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0)
                errx(1, "ioctl SNDCTL_DSP_GETODELAY failed");

        size = ((sample_rate * duration_ms) + 999) / 1000;
        buffer = malloc(sizeof(buffer[0]) * size);
        if (buffer == NULL)
                errx(1, "out of memory");

        /* compute slope duration in samples */
        slope = (DURATION_MIN * sample_rate) / 2000;

        /* compute base gain */
        a = powf(65536.0f, (float)gain / (float)GAIN_MAX) / 65536.0f;

        /* set initial phase and delta */
        p = 0;
        d = (float)frequency / (float)sample_rate;

        /* compute wave */
        for (p = off = 0; off != size; off++, p += d) {
                float sample;

                p = p - floorf(p);
                sample = a * wave_function_16(p, WAVE_POWER);

                if (off < slope)
                        sample = sample * off / (float)slope;
                else if (off > (size - slope))
                        sample = sample * (size - off - 1) / (float)slope;

                buffer[off] = sample;
        }



    printf("Connected to display: %s\n", DisplayString(dpy));
    printf("Logging bell events to: %s\n", LOG_FILE);

    /* Initialize XKB extension */
    if (!XkbQueryExtension(dpy, NULL, &xkb_event_base, &xkb_error_base,
                          &major, &minor)) {
        fprintf(stderr, "XKB extension not available\n");
        XCloseDisplay(dpy);
        exit(1);
    }

    printf("XKB extension initialized (version %d.%d)\n", major, minor);

    /* Select for XKB bell events */
    if (!XkbSelectEvents(dpy, XkbUseCoreKbd, XkbBellNotifyMask,
                        XkbBellNotifyMask)) {
        fprintf(stderr, "Failed to select XKB bell events\n");
        XCloseDisplay(dpy);
        exit(1);
    }

    /* Disable the actual bell sound */
    XkbChangeEnabledControls(dpy, XkbUseCoreKbd, XkbAudibleBellMask, 0);

    log_bell_event("XBell monitor started with WAV playback");
    printf("\nMonitoring XBell events... Press Ctrl+C to exit\n");
    printf("Test with: xkbbell -display %s\n\n", DisplayString(dpy));

    /* Main event loop */
    while (1) {
        XNextEvent(dpy, &event);

        if (event.type == xkb_event_base) {
            handle_xkb_event(dpy, &event);
        }
    }

    /* Cleanup */
    XCloseDisplay(dpy);
    return 0;
}

le / deleted the AddConnectionWatch stuff as it is not needed
 
But the hardware should be able to do it.
Can be. May be not. I don't actually know how the mainboard speaker is driven.
If I had to do it, I'd simply put an analog signal at a fixed frequency to it - this may actually be a pulse, but I doubt the chip is capable for its duty cycle can be changed via software (if, maybe via I2C.)
But I wouldn't design it PWM capable. The current for this speaker is so low, a PWM wasn't worth the effort, while cost more than some simple pulse generator.
I don't know.

Anyway, as I said in my first post:
My PC speakers have been all completely mute, unless something with the hardware was wrong.
So to me that's looking in the wrong place, unless one actually wants to use the PC speaker, for which I also don't see no real use.
 
the zx spectrum speaker was similar with the pc one just that the "oscilator" was software controlled so you could control the pulse width but the downside was it cpu was kind of busy even when playing a single note. the adc (tape recorder input) was similar 1 bit. you could record several seconds of audio with crappy quality ( 1 bit dac and 1 bit adc) :D
 
this does what you want
build with
cc -I /usr/local/include/ -L /usr/local/lib -lX11 -lpthread -lm y.c -o y
run ./y in another term
the beep code is taken from beep.c (/usr/bin/beep) and the programs accepts the same args as beep(1) (except background run)
the X part is AI generated but it works (code is similar with those of pulse audion x11-bell-mod
i tested it an it works
actually it could play a custom wav file but i couldn't find a proper one to download fast so i pulled the beep(1) code in
you can cut some sounds from "the computer error song"
View: https://www.youtube.com/watch?v=mKkLjJHwRec
 
cracauer@, you have been mostly right, and I wasn't completely wrong.

Since the games I played in the 80s on my PC1512 (8086, IBM compatible PC) provided something like music without a soundcard, made me suspicious.
As always Wikipedia brought up a quick explanation:
The PC speaker can be indeed controlled via interrupt to be switched on/off. This way different frequencies are possible. But a real PWM, which was needed to change the volume, was more or less only theoretical possible, but not pratically usable, since it took too much computation power to do anything else then.
Which also covers what covacat said.
This may be of course today no issue anymore.
So, let's simplify and say: You were right, and I was wrong.

However, as I said, to me this seems missing the overall point about the issue.
 
Back
Top