Mercurial > mplayer.hg
view libao2/ao_alsa9.c @ 9278:caea8ed36b48
The reason why mplayer crashes (in some cases) when using x11
output and -wid (>0) parameter is this:
Mplayer by default creates a colormap using DirectColor visual. If the
window given to mplayer uses TrueColor visual there will be an error
when mplayer sets the colormap for the window. This patch
modifies mplayer to use TrueColor visual if the window given to mplayer
uses TrueColor. Another solution is to make sure that the window given to
mplayer is created using DirectColor visual if it is supported by the
display.
Jouni Tulkki <jitulkki@cc.hut.fi>
author | arpi |
---|---|
date | Tue, 04 Feb 2003 18:31:44 +0000 |
parents | b8551709823d |
children | f023b30c80e5 |
line wrap: on
line source
/* ao_alsa9 - ALSA-0.9.x output plugin for MPlayer (C) Alex Beregszaszi <alex@naxine.org> modified for real alsa-0.9.0-support by Joy Winter <joy@pingfm.org> additional AC3 passthrough support by Andy Lo A Foe <andy@alsaplayer.org> 08/22/2002 iec958-init rewritten and merged with common init, joy Any bugreports regarding to this driver are welcome. */ #include <errno.h> #include <sys/time.h> #include <stdlib.h> #include <math.h> #include <string.h> #include <sys/poll.h> #include "../config.h" #if HAVE_SYS_ASOUNDLIB_H #include <sys/asoundlib.h> #elif HAVE_ALSA_ASOUNDLIB_H #include <alsa/asoundlib.h> #else #error "asoundlib.h is not in sys/ or alsa/ - please bugreport" #endif #include "audio_out.h" #include "audio_out_internal.h" #include "afmt.h" extern int verbose; static ao_info_t info = { "ALSA-0.9.x audio output", "alsa9", "Alex Beregszaszi <alex@naxine.org>, Joy Winter <joy@pingfm.org>", "under developement" }; LIBAO_EXTERN(alsa9) static snd_pcm_t *alsa_handler; static snd_pcm_format_t alsa_format; static snd_pcm_hw_params_t *alsa_hwparams; static snd_pcm_sw_params_t *alsa_swparams; static char *alsa_device; /* possible 4096, original 8192 * was only needed for calculating chunksize? */ static int alsa_fragsize = 4096; /* 16 sets buffersize to 16 * chunksize is as default 1024 * which seems to be good avarge for most situations * so buffersize is 16384 frames by default */ static int alsa_fragcount = 16; static int chunk_size = 1024; //is alsa_fragsize / 4 #define MIN_CHUNK_SIZE 1024 static size_t bits_per_sample, bytes_per_sample, bits_per_frame; static size_t chunk_bytes; int ao_mmap = 0; int ao_noblock = 0; int first = 1; static int open_mode; static int set_block_mode; #define ALSA_DEVICE_SIZE 48 #undef BUFFERTIME #define SET_CHUNKSIZE #undef USE_POLL /* to set/get/query special features/parameters */ static int control(int cmd, int arg) { switch(cmd) { case AOCONTROL_QUERY_FORMAT: return CONTROL_TRUE; case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: #ifndef WORDS_BIGENDIAN { //seems to be a problem on macs? { ao_control_vol_t *vol = (ao_control_vol_t *)arg; int err; snd_mixer_t *handle; snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; const char *mix_name = "PCM"; char *card = "default"; long pmin, pmax; long get_vol, set_vol; float calc_vol, diff, f_multi; if(ao_data.format == AFMT_AC3) return CONTROL_TRUE; //allocate simple id snd_mixer_selem_id_alloca(&sid); //sets simple-mixer index and name snd_mixer_selem_id_set_index(sid, 0); snd_mixer_selem_id_set_name(sid, mix_name); if ((err = snd_mixer_open(&handle, 0)) < 0) { printf("alsa-control: mixer open error: %s\n", snd_strerror(err)); return CONTROL_ERROR; } if ((err = snd_mixer_attach(handle, card)) < 0) { printf("alsa-control: mixer attach %s error: %s", card, snd_strerror(err)); snd_mixer_close(handle); return CONTROL_ERROR; } if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { printf("alsa-control: mixer register error: %s", snd_strerror(err)); snd_mixer_close(handle); return CONTROL_ERROR; } err = snd_mixer_load(handle); if (err < 0) { printf("alsa-control: mixer load error: %s", snd_strerror(err)); snd_mixer_close(handle); return CONTROL_ERROR; } elem = snd_mixer_find_selem(handle, sid); if (!elem) { printf("alsa-control: unable to find simple control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); snd_mixer_close(handle); return CONTROL_ERROR; } snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax); f_multi = (100 / (float)pmax); if (cmd == AOCONTROL_SET_VOLUME) { diff = (vol->left+vol->right) / 2; set_vol = rint(diff / f_multi); if (set_vol < 0) set_vol = 0; else if (set_vol > pmax) set_vol = pmax; //setting channels if ((err = snd_mixer_selem_set_playback_volume(elem, 0, set_vol)) < 0) { printf("alsa-control: error setting left channel, %s",snd_strerror(err)); return CONTROL_ERROR; } if ((err = snd_mixer_selem_set_playback_volume(elem, 1, set_vol)) < 0) { printf("alsa-control: error setting right channel, %s",snd_strerror(err)); return CONTROL_ERROR; } //printf("diff=%f, set_vol=%i, pmax=%i, mult=%f\n", diff, set_vol, pmax, f_multi); } else { snd_mixer_selem_get_playback_volume(elem, 0, &get_vol); calc_vol = get_vol; calc_vol = rintf(calc_vol * f_multi); vol->left = vol->right = (int)calc_vol; //printf("get_vol = %i, calc=%i\n",get_vol, calc_vol); } snd_mixer_close(handle); return CONTROL_OK; } }// end big-endian #endif #ifdef WORDS_BIGENDIAN { { return (CONTROL_UNKNOWN); } } #endif } //end witch return(CONTROL_UNKNOWN); } /* open & setup audio device return: 1=success 0=fail */ static int init(int rate_hz, int channels, int format, int flags) { int err; int cards = -1; int period_val; snd_pcm_info_t *alsa_info; char *str_block_mode; int device_set = 0; printf("alsa-init: testing and bugreports are welcome.\n"); printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz, channels, audio_out_format_name(format)); alsa_handler = NULL; if (verbose>0) printf("alsa-init: compiled for ALSA-%s\n", SND_LIB_VERSION_STR); if ((err = snd_card_next(&cards)) < 0 || cards < 0) { printf("alsa-init: no soundcards found: %s\n", snd_strerror(err)); return(0); } ao_data.samplerate = rate_hz; ao_data.bps = channels * rate_hz; ao_data.format = format; ao_data.channels = channels; ao_data.outburst = OUTBURST; //ao_data.buffersize = MAX_OUTBURST; // was 16384 switch (format) { case AFMT_S8: alsa_format = SND_PCM_FORMAT_S8; break; case AFMT_U8: alsa_format = SND_PCM_FORMAT_U8; break; case AFMT_U16_LE: alsa_format = SND_PCM_FORMAT_U16_LE; break; case AFMT_U16_BE: alsa_format = SND_PCM_FORMAT_U16_BE; break; #ifndef WORDS_BIGENDIAN case AFMT_AC3: #endif case AFMT_S16_LE: alsa_format = SND_PCM_FORMAT_S16_LE; break; #ifdef WORDS_BIGENDIAN case AFMT_AC3: #endif case AFMT_S16_BE: alsa_format = SND_PCM_FORMAT_S16_BE; break; case AFMT_S32_LE: alsa_format = SND_PCM_FORMAT_S32_LE; break; case AFMT_S32_BE: alsa_format = SND_PCM_FORMAT_S32_BE; break; default: alsa_format = SND_PCM_FORMAT_MPEG; break; } switch(alsa_format) { case SND_PCM_FORMAT_S16_LE: case SND_PCM_FORMAT_U16_LE: ao_data.bps *= 2; break; case SND_PCM_FORMAT_S32_LE: case SND_PCM_FORMAT_S32_BE: ao_data.bps *= 4; break; case -1: printf("alsa-init: invalid format (%s) requested - output disabled\n", audio_out_format_name(format)); return(0); default: break; } bytes_per_sample = ao_data.bps / ao_data.samplerate; if (ao_subdevice) { //start parsing ao_subdevice, ugly and not thread safe! //maybe there's a better way? int i2 = 1; int i3 = 0; char *sub_str; char *token_str[3]; char* test_str = strdup(ao_subdevice); if ((strcspn(ao_subdevice, ":")) > 0) { sub_str = strtok(test_str, ":"); *(token_str) = sub_str; while (((sub_str = strtok(NULL, ":")) != NULL) && (i2 <= 3)) { *(token_str+i2) = sub_str; i2 += 1; } for (i3=0; i3 <= i2-1; i3++) { if (strcmp(*(token_str + i3), "mmap") == 0) { ao_mmap = 1; } else if (strcmp(*(token_str+i3), "noblock") == 0) { ao_noblock = 1; } else if (strcmp(*(token_str+i3), "hw") == 0) { if ((i3 < i2-1) && (strcmp(*(token_str+i3+1), "noblock") != 0) && (strcmp(*(token_str+i3+1), "mmap") != 0)) { alsa_device = alloca(ALSA_DEVICE_SIZE); snprintf(alsa_device, ALSA_DEVICE_SIZE, "hw:%s", *(token_str+(i3+1))); device_set = 1; } else { alsa_device = *(token_str+i3); device_set = 1; } } else if (device_set == 0 && (!ao_mmap || !ao_noblock)) { alsa_device = *(token_str+i3); device_set = 1; } } } } //end parsing ao_subdevice /* switch for spdif * sets opening sequence for SPDIF * sets also the playback and other switches 'on the fly' * while opening the abstract alias for the spdif subdevice * 'iec958' */ if (format == AFMT_AC3) { char devstr[128]; unsigned char s[4]; int err, c; switch (channels) { case 1: case 2: s[0] = IEC958_AES0_NONAUDIO | IEC958_AES0_CON_EMPHASIS_NONE; s[1] = IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER; s[2] = 0; s[3] = IEC958_AES3_CON_FS_48000; sprintf(devstr, "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x", s[0], s[1], s[2], s[3]); if (verbose>0) printf("alsa-spdif-init: playing AC3, %i channels\n", channels); break; case 4: strcpy(devstr, "surround40"); break; case 6: strcpy(devstr, "surround51"); break; default: fprintf(stderr, "%d channels are not supported\n", channels); exit (0); } alsa_device = devstr; } if (alsa_device == NULL) { int tmp_device, tmp_subdevice, err; if ((err = snd_pcm_info_malloc(&alsa_info)) < 0) { printf("alsa-init: memory allocation error: %s\n", snd_strerror(err)); return(0); } if ((alsa_device = alloca(ALSA_DEVICE_SIZE)) == NULL) { printf("alsa-init: memory allocation error: %s\n", strerror(errno)); return(0); } if ((tmp_device = snd_pcm_info_get_device(alsa_info)) < 0) { printf("alsa-init: cant get device\n"); return(0); } if ((tmp_subdevice = snd_pcm_info_get_subdevice(alsa_info)) < 0) { printf("alsa-init: cant get subdevice\n"); return(0); } if (verbose>0) printf("alsa-init: got device=%i, subdevice=%i\n", tmp_device, tmp_subdevice); if ((err = snprintf(alsa_device, ALSA_DEVICE_SIZE, "hw:%1d,%1d", tmp_device, tmp_subdevice)) <= 0) { printf("alsa-init: cant wrote device-id\n"); } snd_pcm_info_free(alsa_info); printf("alsa-init: %d soundcard%s found, using: %s\n", cards+1, (cards >= 0) ? "" : "s", alsa_device); } else if (strcmp(alsa_device, "help") == 0) { printf("alsa-help: available options are:\n"); printf(" mmap: sets mmap-mode\n"); printf(" noblock: sets noblock-mode\n"); printf(" device-name: sets device name\n"); printf(" example -ao alsa9:mmap:noblock:hw:0,3 sets noblock-mode,\n"); printf(" mmap-mode and the device-name as first card third device\n"); return(0); } else { printf("alsa-init: soundcard set to %s\n", alsa_device); } //setting modes for block or nonblock-mode if (ao_noblock) { open_mode = SND_PCM_NONBLOCK; set_block_mode = 1; str_block_mode = "nonblock-mode"; } else { open_mode = 0; set_block_mode = 0; str_block_mode = "block-mode"; } //sets buff/chunksize if its set manually if (ao_data.buffersize) { switch (ao_data.buffersize) { case 1: alsa_fragcount = 16; chunk_size = 512; if (verbose>0) { printf("alsa-init: buffersize set manually to 8192\n"); printf("alsa-init: chunksize set manually to 512\n"); } break; case 2: alsa_fragcount = 8; chunk_size = 1024; if (verbose>0) { printf("alsa-init: buffersize set manually to 8192\n"); printf("alsa-init: chunksize set manually to 1024\n"); } break; case 3: alsa_fragcount = 32; chunk_size = 512; if (verbose>0) { printf("alsa-init: buffersize set manually to 16384\n"); printf("alsa-init: chunksize set manually to 512\n"); } break; case 4: alsa_fragcount = 16; chunk_size = 1024; if (verbose>0) { printf("alsa-init: buffersize set manually to 16384\n"); printf("alsa-init: chunksize set manually to 1024\n"); } break; default: alsa_fragcount = 16; if (ao_mmap) chunk_size = 512; else chunk_size = 1024; break; } } if (!alsa_handler) { //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, open_mode)) < 0) { if (ao_noblock) { printf("alsa-init: open in nonblock-mode failed, trying to open in block-mode\n"); if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { printf("alsa-init: playback open error: %s\n", snd_strerror(err)); return(0); } else { set_block_mode = 0; str_block_mode = "block-mode"; } } else { printf("alsa-init: playback open error: %s\n", snd_strerror(err)); return(0); } } if ((err = snd_pcm_nonblock(alsa_handler, set_block_mode)) < 0) { printf("alsa-init: error set block-mode %s\n", snd_strerror(err)); } else if (verbose>0) { printf("alsa-init: pcm opend in %s\n", str_block_mode); } snd_pcm_hw_params_alloca(&alsa_hwparams); snd_pcm_sw_params_alloca(&alsa_swparams); // setting hw-parameters if ((err = snd_pcm_hw_params_any(alsa_handler, alsa_hwparams)) < 0) { printf("alsa-init: unable to get initial parameters: %s\n", snd_strerror(err)); return(0); } if (ao_mmap) { snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof()); snd_pcm_access_mask_none(mask); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX); err = snd_pcm_hw_params_set_access_mask(alsa_handler, alsa_hwparams, mask); printf("alsa-init: mmap set\n"); } else { err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,SND_PCM_ACCESS_RW_INTERLEAVED); } if (err < 0) { printf("alsa-init: unable to set access type: %s\n", snd_strerror(err)); return (0); } if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams, alsa_format)) < 0) { printf("alsa-init: unable to set format: %s\n", snd_strerror(err)); return(0); } if ((err = snd_pcm_hw_params_set_channels(alsa_handler, alsa_hwparams, ao_data.channels)) < 0) { printf("alsa-init: unable to set channels: %s\n", snd_strerror(err)); return(0); } if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams, ao_data.samplerate, 0)) < 0) { printf("alsa-init: unable to set samplerate-2: %s\n", snd_strerror(err)); return(0); } #ifdef BUFFERTIME { int alsa_buffer_time = 500000; /* original 60 */ if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams, alsa_buffer_time, 0)) < 0) { printf("alsa-init: unable to set buffer time near: %s\n", snd_strerror(err)); return(0); } else alsa_buffer_time = err; if ((err = snd_pcm_hw_params_set_period_time_near(alsa_handler, alsa_hwparams, alsa_buffer_time/4, 0)) < 0) /* original: alsa_buffer_time/ao_data.bps */ { printf("alsa-init: unable to set period time: %s\n", snd_strerror(err)); return(0); } if (verbose>0) printf("alsa-init: buffer_time: %d, period_time :%d\n",alsa_buffer_time, err); } #endif #ifdef SET_CHUNKSIZE { //set chunksize if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, chunk_size, 0)) < 0) { printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err)); return(0); } else if (verbose>0) { printf("alsa-init: chunksize set to %i\n", chunk_size); } //set period_count if ((period_val = snd_pcm_hw_params_get_periods_max(alsa_hwparams, 0)) < alsa_fragcount) { alsa_fragcount = period_val; } if (verbose>0) printf("alsa-init: current val=%i, fragcount=%i\n", period_val, alsa_fragcount); if ((err = snd_pcm_hw_params_set_periods(alsa_handler, alsa_hwparams, alsa_fragcount, 0)) < 0) { printf("alsa-init: unable to set periods: %s\n", snd_strerror(err)); } } #endif /* finally install hardware parameters */ if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0) { printf("alsa-init: unable to set hw-parameters: %s\n", snd_strerror(err)); return(0); } // end setting hw-params // gets buffersize for control if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams)) < 0) { printf("alsa-init: unable to get buffersize: %s\n", snd_strerror(err)); return(0); } else { ao_data.buffersize = err * bytes_per_sample; if (verbose>0) printf("alsa-init: got buffersize=%i\n", ao_data.buffersize); } // setting sw-params (only avail-min) if noblocking mode was choosed if (ao_noblock) { if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) { printf("alsa-init: unable to get parameters: %s\n",snd_strerror(err)); return(0); } //set min available frames to consider pcm ready (4) //increased for nonblock-mode should be set dynamically later if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, 4)) < 0) { printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err)); return(0); } if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) { printf("alsa-init: unable to install sw-params\n"); return(0); } bits_per_sample = snd_pcm_format_physical_width(alsa_format); bits_per_frame = bits_per_sample * channels; chunk_bytes = chunk_size * bits_per_frame / 8; if (verbose>0) { printf("alsa-init: bits per sample (bps)=%i, bits per frame (bpf)=%i, chunk_bytes=%i\n",bits_per_sample,bits_per_frame,chunk_bytes);} }//end swparams if ((err = snd_pcm_prepare(alsa_handler)) < 0) { printf("alsa-init: pcm prepare error: %s\n", snd_strerror(err)); return(0); } printf("alsa9: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n", ao_data.samplerate, ao_data.channels, bytes_per_sample, ao_data.buffersize, snd_pcm_format_description(alsa_format)); } // end switch alsa_handler (spdif) return(1); } // end init /* close audio device */ static void uninit() { if (alsa_handler) { int err; if (!ao_noblock) { if ((err = snd_pcm_drain(alsa_handler)) < 0) { printf("alsa-uninit: pcm drain error: %s\n", snd_strerror(err)); return; } } if ((err = snd_pcm_close(alsa_handler)) < 0) { printf("alsa-uninit: pcm close error: %s\n", snd_strerror(err)); return; } else { alsa_handler = NULL; alsa_device = NULL; printf("alsa-uninit: pcm closed\n"); } } else { printf("alsa-uninit: no handler defined!\n"); } } static void audio_pause() { int err; if (!ao_noblock) { //drain causes error in nonblock-mode! if ((err = snd_pcm_drain(alsa_handler)) < 0) { printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err)); return; } } else { if (verbose>0) printf("alsa-pause: paused nonblock\n"); return; } } static void audio_resume() { int err; if ((err = snd_pcm_prepare(alsa_handler)) < 0) { printf("alsa-resume: pcm prepare error: %s\n", snd_strerror(err)); return; } } /* stop playing and empty buffers (for seeking/pause) */ static void reset() { int err; if (!ao_noblock) { //drain causes error in nonblock-mode! if ((err = snd_pcm_drain(alsa_handler)) < 0) { printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err)); return; } if ((err = snd_pcm_prepare(alsa_handler)) < 0) { printf("alsa-reset: pcm prepare error: %s\n", snd_strerror(err)); return; } } else { if (verbose>0) printf("alsa-reset: reset nonblocked"); return; } } #ifdef USE_POLL static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count) { unsigned short revents; while (1) { poll(ufds, count, -1); snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents); if (revents & POLLERR) return -EIO; if (revents & POLLOUT) return 0; } } #endif #ifndef timersub #define timersub(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \ } while (0) #endif /* I/O error handler */ static int xrun(u_char *str_mode) { int err; snd_pcm_status_t *status; snd_pcm_status_alloca(&status); if ((err = snd_pcm_status(alsa_handler, status))<0) { printf("status error: %s", snd_strerror(err)); return(0); } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) { struct timeval now, diff, tstamp; gettimeofday(&now, 0); snd_pcm_status_get_trigger_tstamp(status, &tstamp); timersub(&now, &tstamp, &diff); printf("alsa-%s: xrun of at least %.3f msecs. resetting stream\n", str_mode, diff.tv_sec * 1000 + diff.tv_usec / 1000.0); } if ((err = snd_pcm_prepare(alsa_handler))<0) { printf("xrun: prepare error: %s", snd_strerror(err)); return(0); } return(1); /* ok, data should be accepted again */ } static int play_normal(void* data, int len); static int play_mmap(void* data, int len); static int play(void* data, int len, int flags) { int result; if (ao_mmap) result = play_mmap(data, len); else result = play_normal(data, len); return result; } /* plays 'len' bytes of 'data' returns: number of bytes played modified last at 29.06.02 by jp thanxs for marius <marius@rospot.com> for giving us the light ;) */ static int play_normal(void* data, int len) { //bytes_per_sample is always 4 for 2 chn S16_LE int num_frames = len / bytes_per_sample; char *output_samples = (char *)data; snd_pcm_sframes_t res = 0; //fprintf(stderr,"alsa-play: frames=%i, len=%i\n",num_frames,len); if (!alsa_handler) { printf("alsa-play: device configuration error"); return 0; } while (num_frames > 0) { res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames); if (res == -EAGAIN) { snd_pcm_wait(alsa_handler, 1000); } else if (res == -EPIPE) { /* underrun */ if (xrun("play") <= 0) { printf("alsa-play: xrun reset error"); return(0); } } else if (res == -ESTRPIPE) { /* suspend */ printf("alsa-play: pcm in suspend mode. trying to resume\n"); while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN) sleep(1); } else if (res < 0) { printf("alsa-play: unknown status, trying to reset soundcard\n"); if ((res = snd_pcm_prepare(alsa_handler)) < 0) { printf("alsa-play: snd prepare error"); return(0); break; } } if (res > 0) { /* output_samples += ao_data.channels * res; */ output_samples += res * bytes_per_sample; num_frames -= res; } } //end while if (res < 0) { printf("alsa-play: write error %s", snd_strerror(res)); return 0; } return res < 0 ? (int)res : len; } /* mmap-mode mainly based on descriptions by Joshua Haberman <joshua@haberman.com> * 'An overview of the ALSA API' http://people.debian.org/~joshua/x66.html * and some help by Paul Davis <pbd@op.net> */ static int play_mmap(void* data, int len) { snd_pcm_sframes_t commitres, frames_available; snd_pcm_uframes_t frames_transmit, size, offset; const snd_pcm_channel_area_t *area; void *outbuffer; int err, result; #ifdef USE_POLL //seems not really be needed struct pollfd *ufds; int count; count = snd_pcm_poll_descriptors_count (alsa_handler); ufds = malloc(sizeof(struct pollfd) * count); snd_pcm_poll_descriptors(alsa_handler, ufds, count); //first wait_for_poll if (err = (wait_for_poll(alsa_handler, ufds, count) < 0)) { if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN || snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) { xrun("play"); } } #endif outbuffer = alloca(ao_data.buffersize); //don't trust get_space() ;) frames_available = snd_pcm_avail_update(alsa_handler) * bytes_per_sample; if (frames_available < 0) xrun("play"); if (frames_available < 4) { if (first) { first = 0; snd_pcm_start(alsa_handler); } else { //FIXME should break and return 0? snd_pcm_wait(alsa_handler, -1); first = 1; } } /* len is simply the available bufferspace got by get_space() * but real avail_buffer in frames is ab/bytes_per_sample */ size = len / bytes_per_sample; //if (verbose) //printf("len: %i size %i, f_avail %i, bps %i ...\n", len, size, frames_available, bytes_per_sample); frames_transmit = size; /* prepare areas and set sw-pointers * frames_transmit returns the real available buffer-size * sometimes != frames_available cause of ringbuffer 'emulation' */ snd_pcm_mmap_begin(alsa_handler, &area, &offset, &frames_transmit); /* this is specific to interleaved streams (or non-interleaved * streams with only one channel) */ outbuffer = ((char *) area->addr + (area->first + area->step * offset) / 8); //8 //write data memcpy(outbuffer, data, (frames_transmit * bytes_per_sample)); commitres = snd_pcm_mmap_commit(alsa_handler, offset, frames_transmit); if (commitres < 0 || commitres != frames_transmit) { if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN || snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) { xrun("play"); } } //if (verbose) //printf("mmap ft: %i, cres: %i\n", frames_transmit, commitres); /* err = snd_pcm_area_copy(&area, offset, &data, offset, len, alsa_format); */ /* if (err < 0) { */ /* printf("area-copy-error\n"); */ /* return 0; */ /* } */ //calculate written frames! result = commitres * bytes_per_sample; /* if (verbose) { */ /* if (len == result) */ /* printf("result: %i, frames written: %i ...\n", result, frames_transmit); */ /* else */ /* printf("result: %i, frames written: %i, result != len ...\n", result, frames_transmit); */ /* } */ //mplayer doesn't like -result if (result < 0) result = 0; #ifdef USE_POLL free(ufds); #endif return result; } /* how many byes are free in the buffer */ static int get_space() { snd_pcm_status_t *status; int ret; char *str_status; //snd_pcm_sframes_t avail_frames = 0; if ((ret = snd_pcm_status_malloc(&status)) < 0) { printf("alsa-space: memory allocation error: %s\n", snd_strerror(ret)); return(0); } if ((ret = snd_pcm_status(alsa_handler, status)) < 0) { printf("alsa-space: cannot get pcm status: %s\n", snd_strerror(ret)); return(0); } switch(snd_pcm_status_get_state(status)) { case SND_PCM_STATE_OPEN: str_status = "open"; case SND_PCM_STATE_PREPARED: if (str_status != "open") { str_status = "prepared"; first = 1; ret = snd_pcm_status_get_avail(status) * bytes_per_sample; if (ret == 0) //ugly workaround for hang in mmap-mode ret = 10; break; } case SND_PCM_STATE_RUNNING: ret = snd_pcm_status_get_avail(status) * bytes_per_sample; //avail_frames = snd_pcm_avail_update(alsa_handler) * bytes_per_sample; if (str_status != "open" && str_status != "prepared") str_status = "running"; break; case SND_PCM_STATE_PAUSED: if (verbose>0) printf("alsa-space: paused"); str_status = "paused"; ret = 0; break; case SND_PCM_STATE_XRUN: xrun("space"); str_status = "xrun"; first = 1; ret = 0; break; default: str_status = "undefined"; ret = snd_pcm_status_get_avail(status) * bytes_per_sample; if (ret <= 0) { xrun("space"); } } if (verbose>0 && str_status != "running") printf("alsa-space: free space = %i, status=%i, %s --\n", ret, status, str_status); snd_pcm_status_free(status); if (ret < 0) { printf("negative value!!\n"); ret = 0; } // workaround for too small value returned if (ret < MIN_CHUNK_SIZE) ret = 0; return(ret); } /* delay in seconds between first and last sample in buffer */ static float get_delay() { if (alsa_handler) { snd_pcm_status_t *status; float ret; if ((ret = snd_pcm_status_malloc(&status)) < 0) { printf("alsa-delay: memory allocation error: %s\n", snd_strerror(ret)); return(0); } if ((ret = snd_pcm_status(alsa_handler, status)) < 0) { printf("alsa-delay: cannot get pcm status: %s\n", snd_strerror(ret)); return(0); } switch(snd_pcm_status_get_state(status)) { case SND_PCM_STATE_OPEN: case SND_PCM_STATE_PREPARED: case SND_PCM_STATE_RUNNING: ret = (float)snd_pcm_status_get_delay(status)/(float)ao_data.samplerate; break; default: ret = 0; } snd_pcm_status_free(status); if (ret < 0) ret = 0; return(ret); } else { return(0); } }