All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Jim C. Brown" <jbrown106@phreaker.net>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] arts support
Date: Wed, 26 Jan 2005 23:01:32 -0500	[thread overview]
Message-ID: <20050127040132.GA22174@jbrown.mylinuxbox.org> (raw)

[-- Attachment #1: Type: text/plain, Size: 526 bytes --]

This is a preliminary version of oss.c that uses libartsc to play sound instead
of using OSS. It doesn't work (sb.o driver under Linux 2.2.5 kernel complains
of DMA/IRQ timeouts, and no sounds play) but it's a start.

To use it, just replace oss.c with this file, and edit the Makefile in i386-softmmu
and append the output of `artsc-config --cflags` to CFLAGS and the output of
`artsc-config --libs` to LIBS, and then recompile.

-- 
Infinite complexity begets infinite beauty.
Infinite precision begets infinite perfection.

[-- Attachment #2: arts.c --]
[-- Type: text/plain, Size: 9412 bytes --]

/*
 * QEMU Arts Audio output driver
 * 
 * Copyright (c) 2003 Vassili Karpov (malc)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "vl.h"

#if !defined(_WIN32) && !defined(__APPLE__)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/soundcard.h> /* for AUD_FMT_* stuff */
#include <artsc.h>

#define MIN(a, b) ((a)>(b)?(b):(a))
#define MAX(a, b) ((a)<(b)?(b):(a))

int qa_errno = 0; /* qemu artsc errno */

#define DEREF(x) (void)x
#define log(...) fprintf (stderr, "artsc: " __VA_ARGS__)
#define ERRFail(...) do {                                       \
    fprintf (stderr, "artsc: " __VA_ARGS__);                      \
    fprintf (stderr, "arts error: %s\n", arts_error_text(qa_errno));  \
    abort ();                                                   \
} while (0)
#define Fail(...) do {                          \
    fprintf (stderr, "artsc: " __VA_ARGS__);      \
    fprintf (stderr, "\n");                     \
    abort ();                                   \
} while (0)

#ifdef DEBUG_OSS
#define lwarn(...) fprintf (stderr, "artsc: " __VA_ARGS__)
#define linfo(...) fprintf (stderr, "artsc: " __VA_ARGS__)
#define ldebug(...) fprintf (stderr, "artsc: " __VA_ARGS__)
#else
#define lwarn(...)
#define linfo(...)
#define ldebug(...)
#endif


static struct {
    int stream_inited;
    arts_stream_t stream;
    int freq;
    int bits16;
    int nchannels;
    int rpos;
    int wpos;
    int live;
    int oss_fmt;
    int bytes_per_second;
    void *buf;
    int bufsize;
    int nfrags;
    int fragsize;
    int old_optr;
    int leftover;
    uint64_t old_ticks;
} oss = { .stream_inited = 0, .buf = NULL };

static struct {
    int nfrags;
    int fragsize;
} conf = {
    .nfrags = 4,
    .fragsize = 4096
};

static enum {DONT, DSP, TID} est = DONT;

static void do_open ()
{
    oss.stream = arts_play_stream(oss.freq, oss.oss_fmt, oss.nchannels, "qemu-arts");
    oss.stream_inited = 1;

    arts_stream_set(oss.stream, ARTS_P_BLOCKING, 0);

    arts_stream_set(oss.stream, ARTS_P_PACKET_SIZE, conf.fragsize);
    arts_stream_set(oss.stream, ARTS_P_PACKET_COUNT, conf.nfrags);

    oss.nfrags = arts_stream_get(oss.stream, ARTS_P_PACKET_COUNT);
    oss.fragsize = arts_stream_get(oss.stream, ARTS_P_PACKET_SIZE);
    oss.bufsize = arts_stream_get(oss.stream, ARTS_P_BUFFER_SIZE);
    oss.old_optr = 0;

    if (oss.buf != NULL)
    {
	oss.buf = realloc(oss.buf, oss.bufsize);
    }
    else
    {
	oss.buf = malloc(oss.bufsize);
    }

    oss.bytes_per_second = (oss.freq << (oss.nchannels >> 1)) << oss.bits16;

    linfo ("bytes per second %d\n", oss.bytes_per_second);

    linfo ("fragments %d, fragstotal %d, fragsize %d, bytes %d, bufsize %d\n",
	   0, /* what is this? */
	   oss.nfrags,
	   oss.fragsize,
	   0, /* what is this? */
	   oss.bufsize);

    oss.rpos = 0;
    oss.wpos = 0;
    oss.live = 0;

}

static void maybe_open (int req_freq, int req_nchannels,
			audfmt_e req_fmt, int force_open)
{
    int oss_fmt, bits16;

    switch (req_fmt) {
    case AUD_FMT_S8:
    case AUD_FMT_U8:
	bits16 = 0;
	oss_fmt = 8;
	break;

    case AUD_FMT_S16:
    case AUD_FMT_U16:
	bits16 = 1;
	oss_fmt = 16;
	break;

    default:
	abort ();
    }

    if (force_open
	|| (!oss.stream_inited)
	|| (oss_fmt != oss.oss_fmt)
	|| (req_nchannels != oss.nchannels)
	|| (req_freq != oss.freq)
	|| (bits16 != oss.bits16)) {
	oss.oss_fmt = oss_fmt;
	oss.nchannels = req_nchannels;
	oss.freq = req_freq;
	oss.bits16 = bits16;
	do_open ();
    }
}

void AUD_reset (int req_freq, int req_nchannels, audfmt_e req_fmt)
{
    maybe_open (req_freq, req_nchannels, req_fmt, 0);
}

void AUD_open (int req_freq, int req_nchannels, audfmt_e req_fmt)
{
    maybe_open (req_freq, req_nchannels, req_fmt, 1);
}

int AUD_write (void *in_buf, int size)
{
    int to_copy;

    to_copy = MIN (oss.bufsize - oss.live, size);
    memcpy(oss.buf, in_buf, to_copy);
    oss.live += to_copy;

    return to_copy;
}

void AUD_run(void)
{
    int to_copy, temp;

    to_copy = oss.live;

    temp = to_copy;

    while (temp) {
	int copy;

	copy = MIN (temp, oss.bufsize - oss.rpos);
	qa_errno = arts_write(oss.stream, oss.buf + oss.rpos, copy);
	if (qa_errno < 0)
	    ERRFail("write error");
        else
            copy = qa_errno;

	oss.rpos += copy;
	if (oss.rpos == oss.bufsize) {
	    oss.rpos = 0;
	}

	temp -= copy;
	oss.live -= copy;
    }
}

#if 0
void AUD_run (void)
{
    int res;
    int bytes;
    struct audio_buf_info abinfo;

    if (0 == oss.live)
	return;

    if (oss.is_mapped) {
	count_info info;

	res = ioctl (oss.fd, SNDCTL_DSP_GETOPTR, &info);
	if (-1 == res) {
	    int err;

	    err = errno;
	    lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err));
	    return;
	}

	if (info.ptr > oss.old_optr) {
	    bytes = info.ptr - oss.old_optr;
	}
	else {
	    bytes = oss.bufsize + info.ptr - oss.old_optr;
	}

	oss.old_optr = info.ptr;
	oss.live -= bytes;
	return;
    }

    res = ioctl (oss.fd, SNDCTL_DSP_GETOSPACE, &abinfo);

    if (-1 == res) {
	int err;

	err = errno;
	lwarn ("SNDCTL_DSP_GETOSPACE failed with %s\n", strerror (err));
    }

    bytes = abinfo.bytes;
    bytes = MIN (oss.live, bytes);
#if 0
    bytes = (bytes / fragsize) * fragsize;
#endif

    while (bytes) {
	int left, play, written;

	left = oss.bufsize - oss.rpos;
	play = MIN (left, bytes);
	written = write (oss.fd, (uint8_t *)oss.buf + oss.rpos, play);

	if (-1 == written) {
	    if (EAGAIN == errno || EINTR == errno) {
		return;
	    }
	    else {
		ERRFail ("write audio");
	    }
	}

	play = written;
	oss.live -= play;
	oss.rpos += play;
	bytes -= play;

	if (oss.rpos == oss.bufsize) {
	    oss.rpos = 0;
	}
    }
}
#endif

#if 0
static int get_dsp_bytes (void)
{
    int res;
    struct count_info info;

    res = ioctl (oss.fd, SNDCTL_DSP_GETOPTR, &info);
    if (-1 == res) {
	int err;

	err = errno;
	lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err));
	return -1;
    }
    else {
	ldebug ("bytes %d\n", info.bytes);
	return info.bytes;
    }
}
#endif

void AUD_adjust_estimate (int leftover)
{
    oss.leftover = leftover;
}

int AUD_get_free (void)
{
    int free, elapsed;

    free = oss.bufsize - oss.live;

    if (0 == free)
	return 0;

    elapsed = free;
/* not sure how to implement this via arts */
#if 0
    switch (est) {
    case DONT:
	break;

    case DSP:
	{
	    static int old_bytes;
	    int bytes;

	    bytes = get_dsp_bytes ();
	    if (bytes <= 0)
		return free;

	    elapsed = bytes - old_bytes;
	    old_bytes = bytes;
	    ldebug ("dsp elapsed %d bytes\n", elapsed);
	    break;
	}

    case TID:
	{
	    uint64_t ticks, delta;
	    uint64_t ua_elapsed;
	    uint64_t al_elapsed;

	    ticks = qemu_get_clock(rt_clock);
	    delta = ticks - oss.old_ticks;
	    oss.old_ticks = ticks;

	    ua_elapsed = (delta * oss.bytes_per_second) / 1000;
	    al_elapsed = ua_elapsed & ~3ULL;

	    ldebug ("tid elapsed %llu bytes\n", ua_elapsed);

	    if (al_elapsed > (uint64_t) INT_MAX)
		elapsed = INT_MAX;
	    else
		elapsed = al_elapsed;

	    elapsed += oss.leftover;
	}
    }
#endif

    if (elapsed > free) {
	lwarn ("audio can not keep up elapsed %d free %d\n", elapsed, free);
	return free;
    }
    else {
	return elapsed;
    }
}

int AUD_get_live (void)
{
    return oss.live;
}

int AUD_get_buffer_size (void)
{
    return oss.bufsize;
}

#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS"

static int get_conf_val (const char *key, int defval)
{
    int val = defval;
    char *strval;

    strval = getenv (key);
    if (strval) {
	val = atoi (strval);
    }

    return val;
}

void AUD_init (void)
{
    qa_errno = arts_init();
    if (qa_errno < 0)
	ERRFail("failed to initialize arts");

    conf.fragsize = get_conf_val (QC_OSS_FRAGSIZE, conf.fragsize);
    conf.nfrags = get_conf_val (QC_OSS_NFRAGS, conf.nfrags);

}

void AUD_cleanup(void)
{
    arts_close_stream(oss.stream);
    oss.stream_inited = 0;
    arts_free();
}

#else

void AUD_run (void)
{
}

int AUD_write (void *in_buf, int size)
{
    return 0;
}

void AUD_reset (int rfreq, int rnchannels, audfmt_e rfmt)
{
}

void AUD_adjust_estimate (int _leftover)
{
}

int AUD_get_free (void)
{
    return 0;
}

int AUD_get_live (void)
{
    return 0;
}

int AUD_get_buffer_size (void)
{
    return 0;
}

void AUD_init (void)
{
}

#endif

             reply	other threads:[~2005-01-27  4:26 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-01-27  4:01 Jim C. Brown [this message]
2005-01-27 22:28 ` [Qemu-devel] arts support malc
2005-01-28  4:35   ` Jim C. Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20050127040132.GA22174@jbrown.mylinuxbox.org \
    --to=jbrown106@phreaker.net \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.