Annotation of uae/src/sd-alsa/sound.c, revision 1.1.1.4

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.