--- uae/src/sd-uss/sound.c 2018/04/24 16:40:30 1.1.1.1 +++ uae/src/sd-uss/sound.c 2018/04/24 17:20:45 1.1.1.11 @@ -1,22 +1,22 @@ - /* + /* * UAE - The Un*x Amiga Emulator - * + * * Support for Linux/USS sound - * + * * Copyright 1997 Bernd Schmidt */ #include "sysconfig.h" #include "sysdeps.h" -#include "config.h" #include "options.h" #include "memory.h" +#include "events.h" #include "custom.h" -#include "audio.h" +#include "newcpu.h" #include "gensound.h" #include "sounddep/sound.h" -#include "events.h" +#include "threaddep/thread.h" #include @@ -28,15 +28,20 @@ #error "Something went wrong during configuration." #endif -int sound_fd; -static int have_sound; +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; -uae_u16 sndbuffer[44100]; -uae_u16 *sndbufpt; +static int which_buffer; +static uae_u16 sndbuffer[2][44100]; +uae_u16 *sndbufpt, *sndbuf_base; int sndbufsize; -static int exact_log2(int v) +static int exact_log2 (int v) { int l = 0; while ((v >>= 1) != 0) @@ -44,54 +49,94 @@ static int exact_log2(int v) return l; } -void close_sound(void) +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); + 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); + } } -int setup_sound(void) +/* Try to determine whether sound is available. This is only for GUI purposes. */ +int setup_sound (void) { sound_fd = open ("/dev/dsp", O_WRONLY); - have_sound = !(sound_fd < 0); - if (!have_sound) + + 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) { - fprintf(stderr, "ioctl failed - can't use sound.\n"); - close(sound_fd); - have_sound = 0; + perror ("ioctl failed - can't use sound"); + close (sound_fd); return 0; } sound_available = 1; + close (sound_fd); return 1; } -int init_sound (void) +static void open_sound (void) { int tmp; int rate; int dspbits; - 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 = 8192; + 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; } - tmp = 0x00040000 + exact_log2(currprefs.sound_maxbsiz); - ioctl (sound_fd, SNDCTL_DSP_SETFRAGMENT, &tmp); - ioctl (sound_fd, SNDCTL_DSP_GETBLKSIZE, &sndbufsize); + 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 = currprefs.sound_bits; + dspbits = 16; ioctl (sound_fd, SNDCTL_DSP_SAMPLESIZE, &dspbits); ioctl (sound_fd, SOUND_PCM_READ_BITS, &dspbits); - if (dspbits != currprefs.sound_bits) { - fprintf(stderr, "Can't use sound with %d bits\n", currprefs.sound_bits); - return 0; + if (dspbits != 16) { + fprintf (stderr, "Can't use sound with 16 bits\n"); + goto out_err; } - tmp = currprefs.stereo; + tmp = 1; ioctl (sound_fd, SNDCTL_DSP_STEREO, &tmp); rate = currprefs.sound_freq; @@ -99,32 +144,94 @@ int init_sound (void) 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); - return 0; + fprintf (stderr, "Can't use sound with desired frequency %d\n", currprefs.sound_freq); + goto out_err; } - sample_evtime = (long)maxhpos * maxvpos * 50 / rate; + 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 (dspbits == 16) { - /* Will this break horribly on bigendian machines? Possible... */ - if (!(formats & AFMT_S16_LE)) - return 0; - init_sound_table16 (); - eventtab[ev_sample].handler = currprefs.stereo ? sample16s_handler : sample16_handler; - } else { - if (!(formats & AFMT_U8)) + 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; - init_sound_table8 (); - eventtab[ev_sample].handler = currprefs.stereo ? sample8s_handler : sample8_handler; + 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; + } } - sound_available = 1; - printf ("Sound driver found and configured for %d bits at %d Hz, buffer is %d bytes\n", - dspbits, rate, sndbufsize); - sndbufpt = sndbuffer; - -#ifdef FRAME_RATE_HACK - vsynctime = vsynctime * 9 / 10; -#endif +} - return 1; +/* 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; }