Source to src/sd-uss/sound.c


Enter a symbol's name here to quickly find it.

 /*
  * UAE - The Un*x Amiga Emulator
  *
  * Support for Linux/USS sound
  *
  * Copyright 1997 Bernd Schmidt
  */

#include "sysconfig.h"
#include "sysdeps.h"

#include "options.h"
#include "memory.h"
#include "events.h"
#include "custom.h"
#include "newcpu.h"
#include "gensound.h"
#include "sounddep/sound.h"
#include "threaddep/thread.h"

#include <sys/ioctl.h>

#ifdef HAVE_SYS_SOUNDCARD_H
#include <sys/soundcard.h>
#elif defined HAVE_MACHINE_SOUNDCARD_H
#include <machine/soundcard.h>
#else
#error "Something went wrong during configuration."
#endif

static smp_comm_pipe to_sound_pipe;
static uae_sem_t sound_comm_sem;

static int sound_fd;
static int have_sound = 0, have_thread = 0;
static int dont_block;
static unsigned long formats;

static int which_buffer;
static uae_u16 sndbuffer[2][44100];
uae_u16 *sndbufpt, *sndbuf_base;
int sndbufsize;

static int exact_log2 (int v)
{
    int l = 0;
    while ((v >>= 1) != 0)
	l++;
    return l;
}

void finish_sound_buffers (void)
{
    dont_block = currprefs.m68k_speed == -1 && (!regs.stopped || active_fs_packets > 0);
    if (!dont_block) {
	write (sound_fd, sndbuffer[which_buffer], sndbufsize);
    } else {
	write_comm_pipe_int (&to_sound_pipe, 2, 1);
	uae_sem_wait (&sound_comm_sem);
    }
    sndbufpt = sndbuf_base = sndbuffer[which_buffer ^= 1];
}

void close_sound (void)
{
    sync_with_sound = 0;
    if (have_sound)
	close (sound_fd);

    if (have_thread) {
	write_comm_pipe_int (&to_sound_pipe, 1, 1);
	uae_sem_wait (&sound_comm_sem);
	uae_sem_destroy (&sound_comm_sem);
    }
}

/* Try to determine whether sound is available.  This is only for GUI purposes.  */
int setup_sound (void)
{
    sound_fd = open ("/dev/dsp", O_WRONLY);

    sound_available = 0;

    if (sound_fd < 0) {
	perror ("Can't open /dev/dsp");
	if (errno == EBUSY) {
	    /* We can hope, can't we ;) */
	    sound_available = 1;
	    return 1;
	}
	return 0;
    }

    if (ioctl (sound_fd, SNDCTL_DSP_GETFMTS, &formats) == -1) {
	perror ("ioctl failed - can't use sound");
	close (sound_fd);
	return 0;
    }

    sound_available = 1;
    close (sound_fd);
    return 1;
}

static void open_sound (void)
{
    int tmp;
    int rate;
    int dspbits;

    sync_with_sound = 0;

    sound_fd = open ("/dev/dsp", O_WRONLY);
    have_sound = !(sound_fd < 0);
    if (! have_sound) {
	perror ("Can't open /dev/dsp");
	if (errno != EBUSY)
	    sound_available = 0;
	return;
    }
    if (ioctl (sound_fd, SNDCTL_DSP_GETFMTS, &formats) == -1) {
	perror ("ioctl failed - can't use sound");
	goto out_err;
    }

    if (currprefs.sound_maxbsiz < 128 || currprefs.sound_maxbsiz > 16384) {
	fprintf (stderr, "Sound buffer size %d out of range.\n", currprefs.sound_maxbsiz);
	currprefs.sound_maxbsiz = 1024;
    }

    dspbits = 16;
    ioctl (sound_fd, SNDCTL_DSP_SAMPLESIZE, &dspbits);
    ioctl (sound_fd, SOUND_PCM_READ_BITS, &dspbits);
    if (dspbits != 16) {
	fprintf (stderr, "Can't use sound with 16 bits\n");
	goto out_err;
    }

    tmp = 1;
    ioctl (sound_fd, SNDCTL_DSP_STEREO, &tmp);

    rate = currprefs.sound_freq;
    ioctl (sound_fd, SNDCTL_DSP_SPEED, &rate);
    ioctl (sound_fd, SOUND_PCM_READ_RATE, &rate);
    /* Some soundcards have a bit of tolerance here. */
    if (rate < currprefs.sound_freq * 90 / 100 || rate > currprefs.sound_freq * 110 / 100) {
	fprintf (stderr, "Can't use sound with desired frequency %d\n", currprefs.sound_freq);
	goto out_err;
    }

    tmp = 1 << exact_log2 (currprefs.sound_maxbsiz);
    while (tmp * 1000 / (rate * 2 * 2) < 6)
	tmp *= 2;
    if (tmp != currprefs.sound_maxbsiz) {
	fprintf (stderr, "Increasing sound buffer size to sane minimum of %d bytes.\n",
		 tmp);
    }
    tmp = 0x00040000 + exact_log2 (tmp);
    ioctl (sound_fd, SNDCTL_DSP_SETFRAGMENT, &tmp);
    ioctl (sound_fd, SNDCTL_DSP_GETBLKSIZE, &sndbufsize);

    obtainedfreq = rate;

    if (!(formats & AFMT_S16_NE))
	goto out_err;
    init_sound_table16 ();
    sample_handler = sample16s_handler;

    sound_available = 1;
    printf ("Sound driver found and configured at %d Hz, buffer is %d bytes (%d ms).\n",
	    rate, sndbufsize, sndbufsize * 1000 / (rate * 2 * 2));
    sndbufpt = sndbuf_base = sndbuffer[which_buffer = 0];

    sync_with_sound = 1;
    return;

  out_err:
    close (sound_fd);
    have_sound = 0;
    return;
}

static void *sound_thread (void *dummy)
{
    uae_u16 *base;
    for (;;) {
	int cmd = read_comm_pipe_int_blocking (&to_sound_pipe);
	int n;

	switch (cmd) {
	case 0:
	    open_sound ();
	    uae_sem_post (&sound_comm_sem);
	    break;
	case 1:
	    uae_sem_post (&sound_comm_sem);
	    return 0;
	case 2:
	    /* If trying for maximum CPU speed, don't block the main
	       thread, instead set the delaying_for_sound variable.  If
	       not trying for maximum CPU speed, synchronize here by
	       delaying the sem_post until after the write.  */
	    delaying_for_sound = dont_block;
	    base = sndbuf_base;
	    if (dont_block)
		uae_sem_post (&sound_comm_sem);

	    write (sound_fd, sndbuf_base, sndbufsize);
	    if (!dont_block)
		uae_sem_post (&sound_comm_sem);

	    delaying_for_sound = 0;
	    break;
	}
    }
}

/* We use a thread so that we can use the time spent waiting for the sound
   driver for executing m68k instructions, rather than just blocking.  */
static void init_sound_thread (void)
{
    uae_thread_id tid;

    init_comm_pipe (&to_sound_pipe, 20, 1);
    uae_sem_init (&sound_comm_sem, 0, 0);
    uae_start_thread (sound_thread, NULL, &tid);
    have_thread = 1;
}

int init_sound (void)
{
    init_sound_thread ();
    write_comm_pipe_int (&to_sound_pipe, 0, 1);
    uae_sem_wait (&sound_comm_sem);

    return have_sound;
}