|
|
1.1.1.2 root 1: /*
1.1 root 2: * UAE - The Un*x Amiga Emulator
1.1.1.2 root 3: *
1.1 root 4: * Support for Linux/ALSA sound
1.1.1.2 root 5: *
1.1 root 6: * Copyright 1997 Bernd Schmidt
7: * Copyright 2004 Heikki Orsila
1.1.1.4 ! root 8: * Copyright 2006-2007 Richard Drummond
1.1 root 9: *
10: * BUGS: certainly
11: * TODO:
12: * - if setup_sound() fails, there may still be hope to get the
13: * sound device, but we totally give up.. see sd-uss.
14: */
15:
16: #include "sysconfig.h"
17: #include "sysdeps.h"
18:
19: #include "options.h"
1.1.1.4 ! root 20: #include "gensound.h"
1.1 root 21: #include "memory.h"
22: #include "events.h"
23: #include "custom.h"
1.1.1.4 ! root 24: #include "newcpu.h"
1.1 root 25: #include "sounddep/sound.h"
1.1.1.4 ! root 26: #include "threaddep/thread.h"
1.1 root 27:
28: #include <alsa/asoundlib.h>
29:
1.1.1.4 ! root 30: static smp_comm_pipe to_sound_pipe;
! 31: static uae_sem_t sound_comm_sem;
! 32:
! 33: static char alsa_device[256] = "default";
! 34: static int alsa_verbose = 0;
! 35:
! 36: static int have_sound = 0, have_thread = 0;
! 37: static int dont_block;
1.1 root 38:
1.1.1.4 ! root 39: static int which_buffer;
! 40: static uae_u16 sndbuffer[2][44100];
! 41: uae_u16 *sndbufpt, *sndbuf_base;
1.1 root 42: int sndbufsize;
43:
1.1.1.4 ! root 44: static snd_pcm_t *alsa_playback_handle = 0;
! 45: static int alsa_to_frames_divisor;
! 46: static snd_pcm_uframes_t period_frames;
! 47:
! 48: /* alsa_xrun_recovery() function is copied from ALSA manual. why the hell did
! 49: * they make ALSA this hard?! i bet 95% of ALSA programmers would like a
! 50: * simpler way to do error handling.. let the 5% use tricky APIs. */
! 51: static int alsa_xrun_recovery (snd_pcm_t *handle, int err)
! 52: {
! 53: if (err == -EPIPE) {
! 54: /* under-run */
! 55: err = snd_pcm_prepare (handle);
! 56: if (err < 0)
! 57: fprintf (stderr, "uae: no recovery with alsa from underrun, prepare failed: %s\n", snd_strerror (err));
! 58: return 0;
! 59: } else if (err == -ESTRPIPE) {
! 60: while ((err = snd_pcm_resume (handle)) == -EAGAIN) {
! 61: /* wait until the suspend flag is released */
! 62: fprintf (stderr, "uae: sleeping for alsa.\n");
! 63: sleep (1);
! 64: }
! 65: if (err < 0) {
! 66: err = snd_pcm_prepare (handle);
! 67: if (err < 0)
! 68: fprintf (stderr, "uae: no recovery with alsa from suspend, prepare failed: %s\n", snd_strerror (err));
! 69: }
! 70: return 0;
! 71: }
! 72: return err;
! 73: }
! 74:
! 75: static void write_sound_frames (uae_u16 *bufbase)
! 76: {
! 77: char *buf = (char *) bufbase;
! 78: int ret;
! 79:
! 80: int frames = period_frames;
! 81: while (frames > 0) {
! 82: ret = snd_pcm_writei (alsa_playback_handle, buf, frames);
! 83: if (ret < 0) {
! 84: if (ret == -EAGAIN || ret == -EINTR)
! 85: continue;
! 86: if (alsa_xrun_recovery (alsa_playback_handle, ret) < 0) {
! 87: fprintf (stderr, "uae: write error with alsa: %s\n", snd_strerror (ret));
! 88: exit (-1);
! 89: }
! 90: continue;
! 91: }
! 92: frames -= ret;
! 93: buf += ret * alsa_to_frames_divisor;
! 94: }
! 95: }
! 96:
! 97: void finish_sound_buffers ()
! 98: {
! 99: dont_block = currprefs.m68k_speed == -1 && (!regs.stopped || active_fs_packets > 0);
! 100: if (!dont_block) {
! 101: write_sound_frames (sndbuf_base);
! 102: } else {
! 103: write_comm_pipe_int (&to_sound_pipe, 2, 1);
! 104: uae_sem_wait (&sound_comm_sem);
! 105: }
! 106:
! 107: sndbufpt = sndbuf_base = sndbuffer[which_buffer ^ 1];
! 108: }
1.1 root 109:
110: void close_sound (void)
111: {
1.1.1.4 ! root 112: sync_with_sound = 0;
! 113: if (alsa_playback_handle) {
! 114: snd_pcm_close (alsa_playback_handle);
! 115: alsa_playback_handle = 0;
! 116: }
! 117: if (have_thread) {
! 118: write_comm_pipe_int (&to_sound_pipe, 1, 1);
! 119: uae_sem_wait (&sound_comm_sem);
! 120: uae_sem_destroy (&sound_comm_sem);
! 121: have_thread = 0;
! 122: }
1.1 root 123: }
124:
1.1.1.4 ! root 125: static int open_sound_device (void)
1.1 root 126: {
1.1.1.4 ! root 127: return snd_pcm_open (&alsa_playback_handle, alsa_device, SND_PCM_STREAM_PLAYBACK, 0);
1.1 root 128: }
129:
130: /* Try to determine whether sound is available. This is only for GUI purposes. */
131: int setup_sound (void)
132: {
1.1.1.4 ! root 133: int err;
! 134: sound_available = 0;
! 135: if ((err = open_sound_device ()) < 0) {
! 136: /* TODO: if the pcm was busy, we should the same as sd-uss does.
! 137: tell the caller that sound is available. in any other
! 138: condition we should just return 0. */
! 139: write_log ("Cannot open audio device: %s.\n", snd_strerror (err));
! 140: return 0;
! 141: }
! 142: snd_pcm_close (alsa_playback_handle);
! 143: alsa_playback_handle = 0;
! 144: sound_available = 1;
! 145: return 1;
! 146: }
! 147:
! 148: static int set_hw_params (snd_pcm_t *pcm, snd_pcm_hw_params_t *hw_params,
! 149: unsigned int *rate, unsigned int channels,
! 150: snd_pcm_format_t format, unsigned int *buffer_time,
! 151: snd_pcm_uframes_t *buffer_frames,
! 152: snd_pcm_uframes_t *per_frames)
! 153: {
! 154: int err;
! 155: unsigned int periods = 2;
! 156:
! 157: err = snd_pcm_hw_params_any (pcm, hw_params);
! 158: if (err < 0)
! 159: return err;
! 160: err = snd_pcm_hw_params_set_access (pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
! 161: if (err < 0)
! 162: return err;
! 163: err = snd_pcm_hw_params_set_format (pcm, hw_params, format);
! 164: if (err < 0)
! 165: return err;
! 166: err = snd_pcm_hw_params_set_channels (pcm, hw_params, channels);
! 167: if (err < 0)
! 168: return err;
! 169: err = snd_pcm_hw_params_set_rate_near (pcm, hw_params, rate, 0);
! 170: if (err < 0)
! 171: return err;
! 172: err = snd_pcm_hw_params_set_buffer_time_near (pcm, hw_params, buffer_time, NULL);
! 173: if (err < 0)
! 174: return err;
! 175: snd_pcm_hw_params_get_buffer_size (hw_params, buffer_frames);
! 176: err = snd_pcm_hw_params_set_periods_near (pcm, hw_params, &periods, NULL);
! 177: if (err < 0)
! 178: return err;
! 179: if (periods == 1)
! 180: return -EINVAL;
! 181: err = snd_pcm_hw_params (pcm, hw_params);
! 182:
! 183: snd_pcm_hw_params_get_period_size (hw_params, per_frames, NULL);
! 184: return 0;
! 185: }
! 186:
! 187: static int set_sw_params (snd_pcm_t * pcm,
! 188: snd_pcm_sw_params_t * sw_params, snd_pcm_uframes_t buffer_frames, snd_pcm_uframes_t period_frames)
! 189: {
! 190: int err;
! 191:
! 192: err = snd_pcm_sw_params_current (pcm, sw_params);
! 193: if (err < 0)
! 194: return err;
! 195: err = snd_pcm_sw_params_set_start_threshold (pcm, sw_params, (buffer_frames / period_frames) * period_frames);
! 196: if (err < 0)
! 197: return err;
! 198: err = snd_pcm_sw_params_set_avail_min (pcm, sw_params, period_frames);
! 199: if (err < 0)
! 200: return err;
! 201: err = snd_pcm_sw_params_set_stop_threshold (pcm, sw_params, buffer_frames);
! 202: if (err < 0)
! 203: return err;
! 204: err = snd_pcm_sw_params_set_xfer_align (pcm, sw_params, 1);
! 205: if (err < 0)
! 206: return err;
! 207: err = snd_pcm_sw_params (pcm, sw_params);
! 208: if (err < 0)
! 209: return err;
1.1 root 210: return 0;
1.1.1.4 ! root 211: }
! 212:
! 213: static void open_sound (void)
! 214: {
! 215: unsigned int rate;
! 216: snd_pcm_format_t format;
! 217: unsigned int channels;
! 218:
! 219: snd_pcm_hw_params_t *hw_params = 0;
! 220: snd_pcm_sw_params_t *sw_params = 0;
! 221: snd_pcm_uframes_t buffer_frames;
! 222: unsigned int buffer_time;
! 223:
! 224: snd_output_t *alsa_out;
! 225:
! 226: int err;
! 227:
! 228: sync_with_sound = 0;
! 229:
! 230: snd_output_stdio_attach (&alsa_out, stderr, 0);
! 231:
! 232: channels = currprefs.sound_stereo ? 2 : 1;
! 233: rate = currprefs.sound_freq;
! 234:
! 235: have_sound = 0;
! 236: alsa_playback_handle = 0;
! 237: if ((err = open_sound_device ()) < 0) {
! 238: write_log ("Cannot open audio device: %s\n", snd_strerror (err));
! 239: goto nosound;
! 240: }
! 241:
! 242: buffer_time = currprefs.sound_maxbsiz * 1000;
! 243:
! 244: while (buffer_time / (rate * 2 * channels) < 6)
! 245: buffer_time *= 2;
! 246: if (buffer_time != currprefs.sound_maxbsiz * 1000) {
! 247: fprintf (stderr, "Increasing sound buffer size to sane minimum of %d bytes.\n",
! 248: buffer_time / 1000);
! 249: }
! 250: if (buffer_time < 1000 || buffer_time > 500000)
! 251: buffer_time = 100000;
! 252:
! 253:
! 254: if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
! 255: write_log ("Cannot allocate hardware parameter structure: %s.\n", snd_strerror (err));
! 256: goto nosound;
! 257: }
! 258: if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
! 259: write_log ("Cannot allocate software parameter structure: %s.\n", snd_strerror (err));
! 260: goto nosound;
! 261: }
! 262:
! 263: format = SND_PCM_FORMAT_S16;
! 264:
! 265: alsa_to_frames_divisor = 2 * channels;
! 266:
! 267: if ((err =
! 268: set_hw_params (alsa_playback_handle, hw_params, &rate, channels, format, &buffer_time, &buffer_frames,
! 269: &period_frames)) < 0) {
! 270: write_log ("Cannot set hw parameters: %s.\n", snd_strerror (err));
! 271: goto nosound;
! 272: }
! 273:
! 274: if ((err = set_sw_params (alsa_playback_handle, sw_params, buffer_frames, period_frames)) < 0) {
! 275: write_log ("Cannot set sw parameters: %s.\n", snd_strerror (err));
! 276: goto nosound;
! 277: }
! 278:
! 279: sndbufsize = period_frames * alsa_to_frames_divisor;
! 280: snd_pcm_hw_params_free (hw_params);
! 281: snd_pcm_sw_params_free (sw_params);
! 282:
! 283: if ((err = snd_pcm_prepare (alsa_playback_handle)) < 0) {
! 284: write_log ("Cannot prepare audio interface for use: %s.\n", snd_strerror (err));
! 285: goto nosound;
! 286: }
! 287:
! 288: obtainedfreq = rate;
! 289:
! 290: init_sound_table16 ();
! 291: sample_handler = currprefs.sound_stereo ? sample16s_handler : sample16_handler;
! 292:
! 293: have_sound = 1;
! 294: sound_available = 1;
! 295:
! 296: write_log ("ALSA: Using device '%s'.\n", alsa_device);
! 297: write_log ("ALSA: Sound configured for %d Hz. Buffer length is %u us, period size %d bytes.\n",
! 298: rate, buffer_time, period_frames * alsa_to_frames_divisor);
! 299:
! 300: if (alsa_verbose)
! 301: snd_pcm_dump (alsa_playback_handle, alsa_out);
! 302:
! 303: sndbufpt = sndbuf_base = sndbuffer[which_buffer = 0];
! 304:
! 305: sync_with_sound = 1;
! 306: return;
! 307:
! 308: nosound:
! 309: have_sound = 0;
! 310: if (hw_params)
! 311: snd_pcm_hw_params_free (hw_params);
! 312: if (sw_params)
! 313: snd_pcm_sw_params_free (sw_params);
! 314:
! 315: close_sound ();
! 316: }
! 317:
! 318: #if 0
! 319: /*
! 320: * Handle audio specific cfgfile options
! 321: */
! 322: void audio_default_options (struct uae_prefs *p)
! 323: {
! 324: strncpy (alsa_device, "default", 256);
! 325: alsa_verbose = 1;
! 326: }
! 327:
! 328: void audio_save_options (FILE * f, const struct uae_prefs *p)
! 329: {
! 330: cfgfile_write (f, "alsa.device=%s\n", alsa_device);
! 331: cfgfile_write (f, "alsa.verbose=%s\n", alsa_verbose ? "true" : "false");
! 332: }
! 333:
! 334: int audio_parse_option (struct uae_prefs *p, const char *option, const char *value)
! 335: {
! 336: return (cfgfile_string (option, value, "device", alsa_device, 256)
! 337: || cfgfile_yesno (option, value, "verbose", &alsa_verbose));
! 338: }
! 339: #endif
! 340:
! 341: static void *sound_thread (void *dummy)
! 342: {
! 343: for (;;) {
! 344: int cmd = read_comm_pipe_int_blocking (&to_sound_pipe);
! 345: int n;
! 346:
! 347: switch (cmd) {
! 348: case 0:
! 349: open_sound ();
! 350: uae_sem_post (&sound_comm_sem);
! 351: break;
! 352: case 1:
! 353: uae_sem_post (&sound_comm_sem);
! 354: return 0;
! 355: case 2:
! 356: /* If trying for maximum CPU speed, don't block the main
! 357: thread, instead set the delaying_for_sound variable. If
! 358: not trying for maximum CPU speed, synchronize here by
! 359: delaying the sem_post until after the write. */
! 360: delaying_for_sound = dont_block;
! 361: if (dont_block)
! 362: uae_sem_post (&sound_comm_sem);
! 363:
! 364: write_sound_frames (sndbuf_base);
! 365: if (!dont_block)
! 366: uae_sem_post (&sound_comm_sem);
! 367:
! 368: delaying_for_sound = 0;
! 369: break;
! 370: }
! 371: }
! 372: }
! 373:
! 374: /* We use a thread so that we can use the time spent waiting for the sound
! 375: driver for executing m68k instructions, rather than just blocking. */
! 376: static void init_sound_thread (void)
! 377: {
! 378: uae_thread_id tid;
! 379:
! 380: init_comm_pipe (&to_sound_pipe, 20, 1);
! 381: uae_sem_init (&sound_comm_sem, 0, 0);
! 382: uae_start_thread (sound_thread, NULL, &tid);
! 383: have_thread = 1;
1.1 root 384: }
385:
386: int init_sound (void)
387: {
1.1.1.4 ! root 388: init_sound_thread ();
! 389: write_comm_pipe_int (&to_sound_pipe, 0, 1);
! 390: uae_sem_wait (&sound_comm_sem);
! 391:
! 392: return have_sound;
1.1 root 393: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.