On Wed, Jul 21, 2010 at 10:27:29AM +0400, Dmitrij D. Czarkoff wrote:
> Hello!
> 
> I've got a Realtec ALC272 codec on my netbook under OpenBSD 4.7 release:
> 
> % dmesg | grep azalia
> azalia0 at pci0 dev 27 function 0 "Intel 82801GB HD Audio" rev 0x02: apic 4
> int 16 (irq 11)
> azalia0: codecs: Realtek ALC272
> audio0 at azalia0
> 
> While it actually works, it has kind a performance problem - the frequent gaps
> and echos on playback. The longer playback lasts, the worse the situation
> goes.
> 
> Interestingly, pausing and continuing playing make the situation better for
> some time, but after that problem is back.
> 
> The situation is worse with music and is slightly better with video regardless
> of audio file codec and player software.
> 
> Any things I can do?

I've been told this by a few people now.  unfortunately, I don't have
any idea where the problem is.

can you try running the following?  save it to a file, let's call
it audrops.c then build it with 'make LDFLAGS=-lm audrops'.

let that run for a while, at least as long as it takes for you to
normally hear drops and echos.  start it with simply ./audrops.  it
should play a steady tone.  does it print much?  do you hear drops
and/or echos?

-- 
jake...@sdf.lonestar.org
SDF Public Access UNIX System - http://sdf.lonestar.org

/*
 * Copyright (c) 2010 Jacob Meuser <jake...@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/audioio.h>
#include <sys/param.h>
#include <sys/time.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <err.h>
#include <stdlib.h>
#include <math.h>

#define DEFAULT_DEVICE  "/dev/audio"

#define AUDIO_BPS(bits) ((bits) <= 8 ? 1 : (((bits) <= 16) ? 2 : 4))

volatile sig_atomic_t quit;
extern char *__progname;

void
usage(void)
{
        fprintf(stderr,
                "Usage: %s [-b blocksize] [-c channels] [-f device]\n"
                "          [-p precision] [-r rate] [-w hiwat]\n",
                __progname);
}

void
sigint(int s)
{
        quit = 1;
}

void
setsig(void)
{
        struct sigaction sa;

        quit = 0;
        sigfillset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        sa.sa_handler = sigint;
        if (sigaction(SIGINT, &sa, NULL) < 0)
                err(1, "sigaction(int) failed");
        if (sigaction(SIGTERM, &sa, NULL) < 0)
                err(1, "sigaction(term) failed");
        if (sigaction(SIGHUP, &sa, NULL) < 0)
                err(1, "sigaction(hup) failed");
}

int
set_params(int fd, u_int precision, u_int rate, u_int channels, u_int hiwat,
    u_int *bpf, size_t *block_size)
{
        audio_info_t info;

        AUDIO_INITINFO(&info);

        info.mode = AUMODE_PLAY;
        info.play.precision = precision;
        info.play.channels = channels;
        info.play.sample_rate = rate;
        info.play.encoding = AUDIO_ENCODING_SLINEAR;
        info.play.block_size = *block_size;

        info.hiwat = hiwat;
        info.lowat = hiwat - 1;

        if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
                warn("AUDIO_SETINFO");
                return 0;
        }

        if (ioctl(fd, AUDIO_GETINFO, &info) < 0) {
                warn("AUDIO_GETINFO");
                return 0;
        }

        if (info.play.precision != precision) {
                warnx("unable to set play precision: tried %u, got %u",
                    precision, info.play.precision);
                return 0;
        }
        if (info.play.channels != channels) {
                warnx("unable to set play channels: tried %u, got %u",
                    channels, info.play.channels);
                return 0;
        }
        if (info.play.sample_rate != rate) {
                warnx("unable to set play sample_rate: tried %u, got %u",
                    rate, info.play.sample_rate);
                return 0;
        }

        *bpf = AUDIO_BPS(info.play.precision) * info.play.channels;
        *block_size = info.play.block_size;

        return 1;
}

int
paint_samples(uint8_t *samples, u_int precision, u_int rate, u_int channels)
{
        float d, m;
        double playfreq = 440.0;
        int16_t v;
        uint8_t *p;
        int i, j;

        m = (1 << (precision - 1)) - 1;
        for (i = 0, p = samples; i < rate; i++) {
                d = m * sinf(((float)i / (float)rate) *
                    (2 * M_PI * playfreq));
                d = rintf(d);
                v = d;

                for (j = 0; j < channels; j++) {
                        switch (precision) {
                        case 8:
                                *p = v;
                                p++;
                                break;
                        case 16:
                                *p = (v & 0x00ff) >> 0;
                                p++;
                                *p = (v & 0xff00) >> 8;
                                p++;
                                break;
                        default:
                                printf("invalid precision\n");
                                return 0;
                        }
                }
        }

        return 1;
}

size_t
run_test(int fd, uint8_t *samples, size_t samples_size, size_t block_size,
    long block_usec)
{
        struct timeval now, last_time, diff;
        uint8_t *play_buf;
        size_t  total_written = 0;
        int     samples_pos = 0, ret, l, l2;

        play_buf = malloc(block_size);
        if (play_buf == NULL)
                err(1, "play_buf=malloc(%lu)", block_size);

        bcopy(samples, play_buf, block_size);
        samples_pos += block_size;

        gettimeofday(&last_time, NULL);

        while (!quit) {
                ret = write(fd, play_buf, block_size);
                gettimeofday(&now, NULL);
                if (ret != block_size)
                        printf("broken write: %d of %lu\n", ret, block_size);
                total_written += (ret > 0) ? ret : 0;
                timersub(&now, &last_time, &diff);
                last_time = now;
                if (diff.tv_usec > block_usec * 2) {
                        printf("late return at %ld.%06ld: %ld > %ld\n",
                            now.tv_sec, now.tv_usec, diff.tv_usec, block_usec);
                } else if (diff.tv_usec * 2 < block_usec) {
                        printf("early return at %ld.%06ld: %ld < %ld\n",
                            now.tv_sec, now.tv_usec, diff.tv_usec, block_usec);
                }
                if (samples_pos + block_size > samples_size) {
                        l = samples_size - samples_pos;
                        bcopy(samples + samples_pos, play_buf, l);
                        l2 = block_size - l;
                        bcopy(samples, play_buf + l, l2);
                        samples_pos = l2;
                } else {
                        bcopy(samples + samples_pos, play_buf, block_size);
                        samples_pos += block_size;
                }
        }

        free(play_buf);

        return(total_written);
}


int
main(int argc, char *argv[])
{
        audio_info_t info;
        struct timeval now, start_time, diff;
        char    dev[MAXPATHLEN];
        unsigned long block_usec, run_usec;
        size_t  block_size = 4096, samples_size, total_written;
        u_int   rate = 48000, channels = 2, precision = 16, hiwat = 2;
        int     fd, bpf, ch, pe, error = 0;
        uint8_t *samples;
        const char *errstr;

        snprintf(dev, sizeof(dev), DEFAULT_DEVICE);

        while ((ch = getopt(argc, argv, "b:c:f:p:r:w:")) != -1) {
                switch (ch) {
                case 'b':
                        block_size = (size_t)strtonum(optarg, 32, 32768, 
&errstr);
                        if (errstr != NULL) {
                                error++;
                                warnx("block_size %s", errstr);
                        }
                        break;
                case 'c':
                        channels = (u_int)strtonum(optarg, 1, 12, &errstr);
                        if (errstr != NULL) {
                                error++;
                                warnx("channels %s", errstr);
                        }
                        break;
                case 'f':
                        snprintf(dev, sizeof(dev), "%s", optarg);
                        break;
                case 'p':
                        precision = (u_int)strtonum(optarg, 8, 32, &errstr);
                        if (errstr != NULL) {
                                error++;
                                warnx("precision %s", errstr);
                        }
                        break;
                case 'r':
                        rate = (u_int)strtonum(optarg, 4000, 192000, &errstr);
                        if (errstr != NULL) {
                                error++;
                                warnx("sample_rate %s", errstr);
                        }
                        break;
                case 'w':
                        hiwat = (u_int)strtonum(optarg, 2, 4096, &errstr);
                        if (errstr != NULL) {
                                error++;
                                warnx("hiwat %s", errstr);
                        }
                        break;
                default:
                        error++;
                        break;
                }
                if (error) {
                        usage();
                        exit(1);
                }
        }
        argc -= optind;
        argv += optind;

        if ((fd = open(dev, O_WRONLY, 0)) == -1)
                err(1, "open(%s)", dev);

        if (!set_params(fd, precision, rate, channels, hiwat,
            &bpf, &block_size))
                exit(1);

        samples_size = rate * bpf;
        samples = malloc(samples_size);
        if (samples == NULL)
                err(1, "samples=malloc(%lu)", samples_size);

        if (!paint_samples(samples, precision, rate, channels))
                errx(1, "paint_samples() failed");

        block_usec = 1000000 * (block_size / bpf) / rate;

        printf("pre=%u ch=%u bpf=%d block_size=%lu rate=%u block_usec=%ld\n",
            precision, channels, bpf, block_size, rate, block_usec);

        setsig();

        gettimeofday(&start_time, NULL);

        total_written = run_test(fd, samples, samples_size, block_size,
            block_usec);

        gettimeofday(&now, NULL);

        if (ioctl(fd, AUDIO_GETINFO, &info) < 0) {
                warn("AUDIO_GETINFO");
                exit(0);
        }

        if (ioctl(fd, AUDIO_PERROR, &pe) < 0) {
                warn("AUDIO_PERROR");
                exit(0);
        }

        close(fd);

        free(samples);

        timersub(&now, &start_time, &diff);

        printf("pre=%u ch=%u bpf=%d block_size=%lu rate=%u block_usec=%ld\n",
            precision, channels, bpf, block_size, rate, block_usec);

        printf("bytes written = %lu\n", total_written);
        printf("bytes processed = %u\n", info.play.samples);
        printf("bytes errors = %u\n", pe * bpf);
        printf("bytes buffered = %u\n", info.play.seek);
        printf("%lu == %u ?\n", total_written,
            info.play.samples + info.play.seek + pe * bpf);
        printf("run time = %ld.%06ld s\n", diff.tv_sec, diff.tv_usec);
        run_usec = diff.tv_sec * 1000000 + diff.tv_usec;
        printf("avg rate = %llu\n", ((unsigned long long)(info.play.samples / 
bpf) * 1000000) / run_usec);

        exit(0);
}

Reply via email to