diff -rc xmms-1.2.10-orig/Output/alsa/alsa.h xmms-1.2.10/Output/alsa/alsa.h *** xmms-1.2.10-orig/Output/alsa/alsa.h 2004-01-11 17:27:26.000000000 +0100 --- xmms-1.2.10/Output/alsa/alsa.h 2006-01-27 00:28:49.000000000 +0100 *************** *** 50,57 **** char *mixer_device; int buffer_time; int period_time; gboolean debug; - gboolean mmap; struct { int left, right; --- 50,57 ---- char *mixer_device; int buffer_time; int period_time; + int thread_buffer_time; gboolean debug; struct { int left, right; *************** *** 65,72 **** void alsa_about(void); void alsa_configure(void); int alsa_get_mixer(snd_mixer_t **mixer, int card); - snd_mixer_elem_t* alsa_get_mixer_elem(snd_mixer_t *mixer, char *name, int index); - int alsa_setup_mixer(void); void alsa_save_config(void); void alsa_get_volume(int *l, int *r); --- 65,70 ---- diff -rc xmms-1.2.10-orig/Output/alsa/audio.c xmms-1.2.10/Output/alsa/audio.c *** xmms-1.2.10-orig/Output/alsa/audio.c 2004-01-28 00:09:39.000000000 +0100 --- xmms-1.2.10/Output/alsa/audio.c 2006-01-27 00:28:49.000000000 +0100 *************** *** 17,52 **** * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "alsa.h" #include #include static snd_pcm_t *alsa_pcm = NULL; - static snd_pcm_status_t *alsa_status = NULL; - static snd_pcm_channel_area_t *areas = NULL; static snd_output_t *logs = NULL; ! static int alsa_bps = 0; ! static guint64 alsa_total_written = 0; /* Set/Get volume */ static snd_mixer_elem_t *pcm_element = NULL; static snd_mixer_t *mixer = NULL; ! static gboolean mmap, force_start, going, paused; ! static gpointer buffer; - static int alsa_can_pause; struct snd_format { unsigned int rate; unsigned int channels; snd_pcm_format_t format; AFormat xmms_format; }; static struct snd_format *inputf = NULL; --- 17,72 ---- * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * CHANGES + * + * 2005.01.05 Takashi Iwai + * Impelemented the multi-threaded mode with an audio-thread. + * Many fixes and cleanups. */ #include "alsa.h" #include + #include #include static snd_pcm_t *alsa_pcm = NULL; static snd_output_t *logs = NULL; ! static guint64 alsa_total_written = 0; /* input bytes */ ! static guint64 alsa_hw_written = 0; /* output bytes */ ! static gint output_time_offset = 0; ! ! /* device buffer/period sizes in bytes */ ! static int hw_buffer_size, hw_period_size; /* in output bytes */ ! static int hw_buffer_size_in, hw_period_size_in; /* in input bytes */ /* Set/Get volume */ static snd_mixer_elem_t *pcm_element = NULL; static snd_mixer_t *mixer = NULL; ! static gboolean going, paused; ! static gboolean alsa_can_pause; ! ! /* for audio thread */ ! static pthread_t audio_thread; /* audio loop thread */ ! static int thread_buffer_size; /* size of intermediate buffer in bytes */ ! static char *thread_buffer; /* audio intermediate buffer */ ! static int rd_index, wr_index; /* current read/write position in int-buffer */ ! static gboolean pause_request; /* pause status currently requested */ ! static gint flush_request; /* flush status (time) currently requested */ struct snd_format { unsigned int rate; unsigned int channels; snd_pcm_format_t format; AFormat xmms_format; + int sample_bits; + int bps; }; static struct snd_format *inputf = NULL; *************** *** 54,61 **** static struct snd_format *outputf = NULL; static int alsa_setup(struct snd_format *f); ! static void alsa_mmap_audio(char *data, int length); ! static void alsa_write_audio(gpointer data, int length); static struct snd_format * snd_format_from_xmms(AFormat fmt, int rate, int channels); --- 74,80 ---- static struct snd_format *outputf = NULL; static int alsa_setup(struct snd_format *f); ! static void alsa_write_audio(char *data, int length); static struct snd_format * snd_format_from_xmms(AFormat fmt, int rate, int channels); *************** *** 71,92 **** } format_table[] = {{FMT_S16_LE, SND_PCM_FORMAT_S16_LE}, {FMT_S16_BE, SND_PCM_FORMAT_S16_BE}, ! {FMT_S16_NE, ! #ifdef WORDS_BIGENDIAN ! SND_PCM_FORMAT_S16_BE ! #else ! SND_PCM_FORMAT_S16_LE ! #endif ! }, {FMT_U16_LE, SND_PCM_FORMAT_U16_LE}, {FMT_U16_BE, SND_PCM_FORMAT_U16_BE}, ! {FMT_U16_NE, ! #ifdef WORDS_BIGENDIAN ! SND_PCM_FORMAT_U16_BE ! #else ! SND_PCM_FORMAT_U16_LE ! #endif ! }, {FMT_U8, SND_PCM_FORMAT_U8}, {FMT_S8, SND_PCM_FORMAT_S8}, }; --- 90,99 ---- } format_table[] = {{FMT_S16_LE, SND_PCM_FORMAT_S16_LE}, {FMT_S16_BE, SND_PCM_FORMAT_S16_BE}, ! {FMT_S16_NE, SND_PCM_FORMAT_S16}, {FMT_U16_LE, SND_PCM_FORMAT_U16_LE}, {FMT_U16_BE, SND_PCM_FORMAT_U16_BE}, ! {FMT_U16_NE, SND_PCM_FORMAT_U16}, {FMT_U8, SND_PCM_FORMAT_U8}, {FMT_S8, SND_PCM_FORMAT_S8}, }; *************** *** 106,281 **** } } ! int alsa_playing(void) ! { ! if (!going || paused) ! return FALSE; ! ! return(snd_pcm_state(alsa_pcm) == SND_PCM_STATE_RUNNING); ! } ! ! static void xrun_recover(void) ! { ! int err; ! ! if (alsa_cfg.debug) ! { ! snd_pcm_status_alloca(&alsa_status); ! if ((err = snd_pcm_status(alsa_pcm, alsa_status)) < 0) ! g_warning("xrun_recover(): snd_pcm_status() failed"); ! else ! { ! printf("Status:\n"); ! snd_pcm_status_dump(alsa_status, logs); ! } ! } ! ! if (snd_pcm_state(alsa_pcm) == SND_PCM_STATE_XRUN) ! { ! if ((err = snd_pcm_prepare(alsa_pcm)) < 0) ! g_warning("xrun_recover(): snd_pcm_prepare() failed."); ! } ! } ! ! static snd_pcm_sframes_t alsa_get_avail(void) ! { ! snd_pcm_sframes_t ret; ! if ((ret = snd_pcm_avail_update(alsa_pcm)) == -EPIPE) ! xrun_recover(); ! else if (ret < 0) ! { ! g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s", ! snd_strerror(-ret)); ! return 0; ! } ! else ! return ret; ! if ((ret = snd_pcm_avail_update(alsa_pcm)) < 0) ! { ! g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s", ! snd_strerror(-ret)); ! return 0; ! } ! return ret; ! } ! ! int alsa_free(void) ! { ! if (paused) ! return 0; ! else ! { ! int err; ! if (force_start && ! snd_pcm_state(alsa_pcm) == SND_PCM_STATE_PREPARED) ! { ! if ((err = snd_pcm_start(alsa_pcm)) < 0) ! g_warning("alsa_free(): snd_pcm_start() " ! "failed: %s", snd_strerror(-err)); ! else ! debug("Stream started"); ! } ! force_start = TRUE; ! ! return snd_pcm_frames_to_bytes(alsa_pcm, alsa_get_avail()); ! } ! } ! ! void alsa_pause(short p) ! { ! debug("alsa_pause"); ! if (p) ! paused = TRUE; ! ! if (alsa_can_pause) ! snd_pcm_pause(alsa_pcm, p); ! else if (p) ! snd_pcm_drop(alsa_pcm); ! ! if (!p) ! paused = FALSE; ! } ! ! void alsa_close(void) ! { ! int err, started; ! ! debug("Closing device"); ! ! started = going; ! going = 0; ! ! pcm_element = NULL; ! ! if (mixer) ! { ! snd_mixer_close(mixer); ! mixer = NULL; ! } ! ! if (alsa_pcm != NULL) ! { ! if (started) ! if ((err = snd_pcm_drop(alsa_pcm)) < 0) ! g_warning("alsa_pcm_drop() failed: %s", ! snd_strerror(-err)); ! ! if ((err = snd_pcm_close(alsa_pcm)) < 0) ! g_warning("alsa_pcm_close() failed: %s", ! snd_strerror(-err)); ! alsa_pcm = NULL; ! } ! ! if (mmap) { ! g_free(buffer); ! buffer = NULL; ! ! g_free(areas); ! areas = NULL; ! } ! ! xmms_convert_buffers_destroy(convertb); ! convertb = NULL; ! g_free(inputf); ! inputf = NULL; ! g_free(effectf); ! effectf = NULL; ! ! alsa_save_config(); ! ! debug("Device closed"); ! } ! ! static void alsa_reopen(struct snd_format *f) ! { ! unsigned int tmp = alsa_get_written_time(); ! ! if (alsa_pcm != NULL) ! { ! snd_pcm_close(alsa_pcm); ! alsa_pcm = NULL; ! } ! ! if (mmap) { ! g_free(buffer); ! buffer = NULL; ! ! g_free(areas); ! areas = NULL; ! } ! ! if (alsa_setup(f) < 0) ! g_warning("Failed to reopen the audio device"); ! ! alsa_total_written = tmp; ! snd_pcm_prepare(alsa_pcm); ! } ! ! void alsa_flush(int time) ! { ! alsa_total_written = (guint64) time * alsa_bps / 1000; ! } ! static void parse_mixer_name(char *str, char **name, int *index) { char *end; --- 113,121 ---- } } ! /* ! * mixer stuff ! */ static void parse_mixer_name(char *str, char **name, int *index) { char *end; *************** *** 337,343 **** } ! snd_mixer_elem_t* alsa_get_mixer_elem(snd_mixer_t *mixer, char *name, int index) { snd_mixer_selem_id_t *selem_id; snd_mixer_elem_t* elem; --- 177,183 ---- } ! static snd_mixer_elem_t* alsa_get_mixer_elem(snd_mixer_t *mixer, char *name, int index) { snd_mixer_selem_id_t *selem_id; snd_mixer_elem_t* elem; *************** *** 353,359 **** return elem; } ! int alsa_setup_mixer(void) { char *name; long int a, b; --- 193,199 ---- return elem; } ! static int alsa_setup_mixer(void) { char *name; long int a, b; *************** *** 406,411 **** --- 246,260 ---- return 0; } + static void alsa_cleanup_mixer(void) + { + pcm_element = NULL; + if (mixer) { + snd_mixer_close(mixer); + mixer = NULL; + } + } + void alsa_get_volume(int *l, int *r) { static gboolean first = TRUE; *************** *** 461,485 **** } int alsa_get_output_time(void) { snd_pcm_sframes_t delay; ! ssize_t db = 0; ! if (!going) return 0; ! if (!snd_pcm_delay(alsa_pcm, &delay)) ! db = snd_pcm_frames_to_bytes(alsa_pcm, delay); ! ! if (db < alsa_total_written) ! return ((alsa_total_written - db) * 1000 / alsa_bps); ! return 0; } int alsa_get_written_time(void) { ! return (alsa_total_written * 1000 / alsa_bps); } #define STEREO_ADJUST(type, type2, endian) \ --- 310,512 ---- } + /* + * audio stuff + */ + + int alsa_playing(void) + { + if (!going || paused || alsa_pcm == NULL) + return FALSE; + + return(snd_pcm_state(alsa_pcm) == SND_PCM_STATE_RUNNING); + } + + + /* handle generic errors */ + static int alsa_handle_error(int err) + { + switch (err) { + case -EPIPE: /* XRUN */ + if (alsa_cfg.debug) { + snd_pcm_status_t *alsa_status; + snd_pcm_status_alloca(&alsa_status); + if (snd_pcm_status(alsa_pcm, alsa_status) < 0) + g_warning("xrun_recover(): snd_pcm_status() failed"); + else { + printf("Status:\n"); + snd_pcm_status_dump(alsa_status, logs); + } + } + return snd_pcm_prepare(alsa_pcm); + + case -ESTRPIPE: /* suspend */ + while ((err = snd_pcm_resume(alsa_pcm)) == -EAGAIN) + sleep(1); /* wait until suspend flag is released */ + if (err < 0) { + g_warning("suspend_recover(): snd_pcm_resume() failed."); + return snd_pcm_prepare(alsa_pcm); + } + break; + } + + return err; + } + + /* update and get the available space on h/w buffer (in frames) */ + static snd_pcm_sframes_t alsa_get_avail(void) + { + snd_pcm_sframes_t ret; + + if (alsa_pcm == NULL) + return 0; + + while ((ret = snd_pcm_avail_update(alsa_pcm)) < 0) { + ret = alsa_handle_error(ret); + if (ret < 0) { + g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s", + snd_strerror(-ret)); + return 0; + } + } + return ret; + } + + /* do pause operation */ + static void alsa_do_pause(gboolean p) + { + if (paused == p) + return; + + if (alsa_pcm) { + if (alsa_can_pause) { + snd_pcm_pause(alsa_pcm, p); + } else if (p) { + snd_pcm_drop(alsa_pcm); + snd_pcm_prepare(alsa_pcm); + } + } + paused = p; + } + + void alsa_pause(short p) + { + debug("alsa_pause"); + pause_request = p; + } + + /* close PCM and release associated resources */ + static void alsa_close_pcm(void) + { + if (alsa_pcm) { + int err; + snd_pcm_drop(alsa_pcm); + if ((err = snd_pcm_close(alsa_pcm)) < 0) + g_warning("alsa_pcm_close() failed: %s", + snd_strerror(-err)); + alsa_pcm = NULL; + } + } + + /* reopen ALSA PCM */ + static int alsa_reopen(struct snd_format *f) + { + /* remember the current position */ + output_time_offset += (alsa_hw_written * 1000) / outputf->bps; + alsa_hw_written = 0; + + alsa_close_pcm(); + + return alsa_setup(f); + } + + /* do flush (drop) operation */ + static void alsa_do_flush(int time) + { + if (alsa_pcm) { + snd_pcm_drop(alsa_pcm); + snd_pcm_prepare(alsa_pcm); + } + /* correct the offset */ + output_time_offset = time; + alsa_total_written = (guint64) time * inputf->bps / 1000; + rd_index = wr_index = alsa_hw_written = 0; + } + + void alsa_flush(int time) + { + flush_request = time; + while (flush_request != -1) + xmms_usleep(10000); + } + + void alsa_close(void) + { + if (! going) + return; + + debug("Closing device"); + + going = 0; + + pthread_join(audio_thread, NULL); + + alsa_cleanup_mixer(); + + xmms_convert_buffers_destroy(convertb); + convertb = NULL; + g_free(inputf); + inputf = NULL; + g_free(effectf); + effectf = NULL; + g_free(outputf); + outputf = NULL; + + alsa_save_config(); + + if (alsa_cfg.debug) + snd_output_close(logs); + debug("Device closed"); + } + + /* return the size of audio data filled in the audio thread buffer */ + static int get_thread_buffer_filled(void) + { + int filled = wr_index - rd_index; + if (filled >= 0) + return filled; + return thread_buffer_size + filled; + } + + /* get the free space on buffer */ + int alsa_free(void) + { + return thread_buffer_size - get_thread_buffer_filled() - 1; + } + int alsa_get_output_time(void) { snd_pcm_sframes_t delay; ! guint64 bytes = 0; ! if (!going || alsa_pcm == NULL) return 0; ! if (!snd_pcm_delay(alsa_pcm, &delay)) { ! bytes = snd_pcm_frames_to_bytes(alsa_pcm, delay); ! if (alsa_hw_written < bytes) ! bytes = 0; ! else ! bytes = alsa_hw_written - bytes; ! } ! return output_time_offset + (bytes * 1000) / outputf->bps; } int alsa_get_written_time(void) { ! if (!going) ! return 0; ! return (alsa_total_written * 1000) / inputf->bps; } #define STEREO_ADJUST(type, type2, endian) \ *************** *** 584,636 **** } ! void alsa_write(gpointer data, int length) { ! EffectPlugin *ep; if (paused) return; ! force_start = FALSE; ! ! if (effects_enabled() && (ep = get_current_effect_plugin())) ! { ! int new_freq = inputf->rate; ! int new_chn = inputf->channels; ! AFormat f = inputf->xmms_format; ! if (ep->query_format) ! { ! ep->query_format(&f, &new_freq, &new_chn); ! ! if (f != effectf->xmms_format || ! new_freq != effectf->rate || ! new_chn != effectf->channels) ! { ! debug("Changing audio format for effect plugin"); ! ! g_free(effectf); ! effectf = snd_format_from_xmms(f, new_freq, ! new_chn); ! alsa_reopen(effectf); ! } ! ! } length = ep->mod_samples(&data, length, inputf->xmms_format, inputf->rate, inputf->channels); } - else if (effectf) - { - g_free(effectf); - effectf = NULL; - effectf = snd_format_from_xmms(inputf->xmms_format, - inputf->rate, - inputf->channels); - alsa_reopen(inputf); - } if (alsa_convert_func != NULL) length = alsa_convert_func(convertb, &data, length); --- 611,657 ---- } ! /* transfer data to audio h/w; length is given in bytes ! * ! * data can be modified via effect plugin, rate conversion or ! * software volume before passed to audio h/w ! */ ! static void alsa_do_write(gpointer data, int length) { ! EffectPlugin *ep = NULL; ! int new_freq; ! int new_chn; ! AFormat f; if (paused) return; ! new_freq = inputf->rate; ! new_chn = inputf->channels; ! f = inputf->xmms_format; ! if (effects_enabled() && (ep = get_current_effect_plugin()) && ! ep->query_format) ! ep->query_format(&f, &new_freq, &new_chn); + if (f != effectf->xmms_format || new_freq != effectf->rate || + new_chn != effectf->channels) { + debug("Changing audio format for effect plugin"); + g_free(effectf); + effectf = snd_format_from_xmms(f, new_freq, new_chn); + if (alsa_reopen(effectf) < 0) { + /* fatal error... */ + alsa_close(); + return; + } + } + + if (ep) { length = ep->mod_samples(&data, length, inputf->xmms_format, inputf->rate, inputf->channels); } if (alsa_convert_func != NULL) length = alsa_convert_func(convertb, &data, length); *************** *** 644,656 **** if (alsa_cfg.soft_volume) volume_adjust(data, length, outputf->xmms_format, outputf->channels); ! if (mmap) ! alsa_mmap_audio(data, length); ! else ! alsa_write_audio(data, length); } ! static void alsa_write_audio(gpointer data, int length) { snd_pcm_sframes_t written_frames; --- 665,693 ---- if (alsa_cfg.soft_volume) volume_adjust(data, length, outputf->xmms_format, outputf->channels); ! alsa_write_audio(data, length); } ! /* write callback */ ! void alsa_write(gpointer data, int length) ! { ! int cnt; ! char *src = (char *)data; ! ! alsa_total_written += length; ! while (length > 0) { ! int wr; ! cnt = MIN(length, thread_buffer_size - wr_index); ! memcpy(thread_buffer + wr_index, src, cnt); ! wr = (wr_index + cnt) % thread_buffer_size; ! wr_index = wr; ! length -= cnt; ! src += cnt; ! } ! } ! ! /* transfer data to audio h/w via normal write */ ! static void alsa_write_audio(char *data, int length) { snd_pcm_sframes_t written_frames; *************** *** 663,735 **** { int written = snd_pcm_frames_to_bytes(alsa_pcm, written_frames); - alsa_total_written += written; length -= written; ! data = (char*) data + written; } ! else if (written_frames == -EPIPE) ! xrun_recover(); ! else ! { ! g_warning("alsa_write_audio(): write error: %s", ! snd_strerror(-written_frames)); ! break; } } } ! static void alsa_mmap_audio(char *data, int length) { ! int cnt = 0, err; ! snd_pcm_uframes_t offset, frames, frame; ! const snd_pcm_channel_area_t *chan_areas = areas; ! int channel_offset = 0, channel; ! ssize_t sample_size, offset_bytes, step; ! ! alsa_get_avail(); ! while (length > 0) ! { ! frames = snd_pcm_bytes_to_frames(alsa_pcm, length); ! if ((err = snd_pcm_mmap_begin(alsa_pcm, &chan_areas, &offset, &frames) < 0)) ! g_warning("alsa_mmap_audio(): snd_pcm_mmap_begin() " ! "failed: %s", snd_strerror(-err)); ! ! cnt = snd_pcm_frames_to_bytes(alsa_pcm, frames); ! ! sample_size = snd_pcm_samples_to_bytes(alsa_pcm, 1); ! step = chan_areas[0].step / 8; ! offset_bytes = offset * step; ! for (frame = 0; frame < frames; frame++) ! { ! for (channel = 0; channel < outputf->channels; channel++) ! { ! char *ptr = chan_areas[channel].addr; ! memcpy(ptr + chan_areas[channel].first / 8 + ! offset_bytes, ! data + channel_offset, sample_size); ! channel_offset += sample_size; } ! offset_bytes += step; } - - err = snd_pcm_mmap_commit(alsa_pcm, offset, frames); - if (err == -EPIPE) - xrun_recover(); - else if (err < 0) - g_warning("alsa_mmap_audio(): snd_pcm_mmap_commit() " - "failed: %s", snd_strerror(-err)); - else if (err != frames) - g_warning("alsa_mmap_audio(): snd_pcm_mmap_commit " - "returned %d, expected %d", err, (int)frames); - - alsa_total_written += cnt; - - length -= cnt; } } int alsa_open(AFormat fmt, int rate, int nch) { debug("Opening device"); --- 700,785 ---- { int written = snd_pcm_frames_to_bytes(alsa_pcm, written_frames); length -= written; ! data += written; ! alsa_hw_written += written; } ! else { ! int err = alsa_handle_error((int)written_frames); ! if (err < 0) { ! g_warning("alsa_write_audio(): write error: %s", ! snd_strerror(-err)); ! break; ! } } } } ! /* transfer audio data from thread buffer to h/w */ ! static void alsa_write_out_thread_data(void) { ! gint length, cnt, avail; ! length = MIN(hw_period_size_in, get_thread_buffer_filled()); ! avail = snd_pcm_frames_to_bytes(alsa_pcm, alsa_get_avail()); ! length = MIN(length, avail); ! while (length > 0) { ! int rd; ! cnt = MIN(length, thread_buffer_size - rd_index); ! alsa_do_write(thread_buffer + rd_index, cnt); ! rd = (rd_index + cnt) % thread_buffer_size; ! rd_index = rd; ! length -= cnt; ! } ! } ! /* audio thread loop */ ! /* FIXME: proper lock? */ ! static void *alsa_loop(void *arg) ! { ! int npfds = snd_pcm_poll_descriptors_count(alsa_pcm); ! struct pollfd *pfds; ! unsigned short *revents; ! ! if (npfds <= 0) ! goto _error; ! pfds = alloca(sizeof(*pfds) * npfds); ! revents = alloca(sizeof(*revents) * npfds); ! while (going && alsa_pcm) { ! if (! paused && get_thread_buffer_filled() > hw_period_size_in) { ! snd_pcm_poll_descriptors(alsa_pcm, pfds, npfds); ! if (poll(pfds, npfds, 10) > 0) { ! /* need to check revents. poll() with dmix returns ! * a postive value even if no data is available ! */ ! int i; ! snd_pcm_poll_descriptors_revents(alsa_pcm, pfds, npfds, revents); ! for (i = 0; i < npfds; i++) ! if (revents[i] & POLLOUT) { ! alsa_write_out_thread_data(); ! break; ! } } ! } else ! xmms_usleep(10000); ! ! if (pause_request != paused) ! alsa_do_pause(pause_request); ! ! if (flush_request != -1) { ! alsa_do_flush(flush_request); ! flush_request = -1; } } + + _error: + alsa_close_pcm(); + g_free(thread_buffer); + thread_buffer = NULL; + pthread_exit(NULL); } + /* open callback */ int alsa_open(AFormat fmt, int rate, int nch) { debug("Opening device"); *************** *** 739,746 **** if (alsa_cfg.debug) snd_output_stdio_attach(&logs, stdout, 0); - mmap = alsa_cfg.mmap; - if (alsa_setup(inputf) < 0) { alsa_close(); --- 789,794 ---- *************** *** 751,763 **** convertb = xmms_convert_buffers_new(); ! alsa_total_written = 0; going = TRUE; paused = FALSE; - force_start = FALSE; - - snd_pcm_prepare(alsa_pcm); return 1; } --- 799,823 ---- convertb = xmms_convert_buffers_new(); ! output_time_offset = 0; ! alsa_total_written = alsa_hw_written = 0; going = TRUE; paused = FALSE; + thread_buffer_size = (guint64)alsa_cfg.thread_buffer_time * inputf->bps / 1000; + if (thread_buffer_size < hw_buffer_size) + thread_buffer_size = hw_buffer_size * 2; + if (thread_buffer_size < 8192) + thread_buffer_size = 8192; + thread_buffer_size += hw_buffer_size; + thread_buffer_size -= thread_buffer_size % hw_period_size; + thread_buffer = g_malloc0(thread_buffer_size); + wr_index = rd_index = 0; + pause_request = FALSE; + flush_request = -1; + + pthread_create(&audio_thread, NULL, alsa_loop, NULL); + return 1; } *************** *** 787,792 **** --- 847,854 ---- f->rate = rate; f->channels = channels; + f->sample_bits = snd_pcm_format_physical_width(f->format); + f->bps = (rate * f->sample_bits * channels) >> 3; return f; } *************** *** 806,812 **** int err; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; ! int alsa_buffer_time, bits_per_sample; unsigned int alsa_period_time; snd_pcm_uframes_t alsa_buffer_size, alsa_period_size; --- 868,874 ---- int err; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; ! int alsa_buffer_time; unsigned int alsa_period_time; snd_pcm_uframes_t alsa_buffer_size, alsa_period_size; *************** *** 816,824 **** alsa_stereo_convert_func = NULL; alsa_frequency_convert_func = NULL; ! outputf = snd_format_from_xmms(effectf->xmms_format, ! effectf->rate, ! effectf->channels); debug("Opening device: %s", alsa_cfg.pcm_device); /* FIXME: Can snd_pcm_open() return EAGAIN? */ --- 878,885 ---- alsa_stereo_convert_func = NULL; alsa_frequency_convert_func = NULL; ! g_free(outputf); ! outputf = snd_format_from_xmms(f->xmms_format, f->rate, f->channels); debug("Opening device: %s", alsa_cfg.pcm_device); /* FIXME: Can snd_pcm_open() return EAGAIN? */ *************** *** 829,838 **** g_warning("alsa_setup(): Failed to open pcm device (%s): %s", alsa_cfg.pcm_device, snd_strerror(-err)); alsa_pcm = NULL; return -1; } - snd_pcm_nonblock(alsa_pcm, FALSE); if (alsa_cfg.debug) { snd_pcm_info_t *info; --- 890,903 ---- g_warning("alsa_setup(): Failed to open pcm device (%s): %s", alsa_cfg.pcm_device, snd_strerror(-err)); alsa_pcm = NULL; + g_free(outputf); + outputf = NULL; return -1; } + /* doesn't care about non-blocking */ + /* snd_pcm_nonblock(alsa_pcm, 0); */ + if (alsa_cfg.debug) { snd_pcm_info_t *info; *************** *** 856,872 **** return -1; } ! if (mmap && ! (err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams, ! SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) ! { ! g_message("alsa_setup(): Cannot set mmap'ed mode: %s. " ! "falling back to direct write", snd_strerror(-err)); ! mmap = 0; ! } ! ! if (!mmap && ! (err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { g_warning("alsa_setup(): Cannot set direct write mode: %s", --- 921,927 ---- return -1; } ! if ((err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { g_warning("alsa_setup(): Cannot set direct write mode: %s", *************** *** 894,910 **** break; } } ! if (outputf->format != effectf->format) { outputf->xmms_format = format_from_alsa(outputf->format); debug("Converting format from %d to %d", ! effectf->xmms_format, outputf->xmms_format); if (outputf->xmms_format < 0) return -1; alsa_convert_func = xmms_convert_get_func(outputf->xmms_format, ! effectf->xmms_format); if (alsa_convert_func == NULL) return -1; } --- 949,965 ---- break; } } ! if (outputf->format != f->format) { outputf->xmms_format = format_from_alsa(outputf->format); debug("Converting format from %d to %d", ! f->xmms_format, outputf->xmms_format); if (outputf->xmms_format < 0) return -1; alsa_convert_func = xmms_convert_get_func(outputf->xmms_format, ! f->xmms_format); if (alsa_convert_func == NULL) return -1; } *************** *** 918,931 **** } snd_pcm_hw_params_set_channels_near(alsa_pcm, hwparams, &outputf->channels); ! if (outputf->channels != effectf->channels) { debug("Converting channels from %d to %d", ! effectf->channels, outputf->channels); alsa_stereo_convert_func = xmms_convert_get_channel_func(outputf->xmms_format, outputf->channels, ! effectf->channels); if (alsa_stereo_convert_func == NULL) return -1; } --- 973,986 ---- } snd_pcm_hw_params_set_channels_near(alsa_pcm, hwparams, &outputf->channels); ! if (outputf->channels != f->channels) { debug("Converting channels from %d to %d", ! f->channels, outputf->channels); alsa_stereo_convert_func = xmms_convert_get_channel_func(outputf->xmms_format, outputf->channels, ! f->channels); if (alsa_stereo_convert_func == NULL) return -1; } *************** *** 936,945 **** g_warning("alsa_setup(): No usable samplerate available."); return -1; } ! if (outputf->rate != effectf->rate) { debug("Converting samplerate from %d to %d", ! effectf->rate, outputf->rate); alsa_frequency_convert_func = xmms_convert_get_frequency_func(outputf->xmms_format, outputf->channels); --- 991,1000 ---- g_warning("alsa_setup(): No usable samplerate available."); return -1; } ! if (outputf->rate != f->rate) { debug("Converting samplerate from %d to %d", ! f->rate, outputf->rate); alsa_frequency_convert_func = xmms_convert_get_frequency_func(outputf->xmms_format, outputf->channels); *************** *** 947,960 **** return -1; } ! alsa_buffer_time = alsa_cfg.buffer_time * 1000; ! if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_pcm, hwparams, ! &alsa_buffer_time, 0)) < 0) ! { ! g_warning("alsa_setup(): Set buffer time failed: %s.", ! snd_strerror(-err)); ! return -1; ! } alsa_period_time = alsa_cfg.period_time * 1000; if ((err = snd_pcm_hw_params_set_period_time_near(alsa_pcm, hwparams, --- 1002,1009 ---- return -1; } ! outputf->sample_bits = snd_pcm_format_physical_width(outputf->format); ! outputf->bps = (outputf->rate * outputf->sample_bits * outputf->channels) >> 3; alsa_period_time = alsa_cfg.period_time * 1000; if ((err = snd_pcm_hw_params_set_period_time_near(alsa_pcm, hwparams, *************** *** 965,970 **** --- 1014,1028 ---- return -1; } + alsa_buffer_time = alsa_cfg.buffer_time * 1000; + if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_pcm, hwparams, + &alsa_buffer_time, 0)) < 0) + { + g_warning("alsa_setup(): Set buffer time failed: %s.", + snd_strerror(-err)); + return -1; + } + if (snd_pcm_hw_params(alsa_pcm, hwparams) < 0) { if (alsa_cfg.debug) *************** *** 1011,1037 **** snd_pcm_dump(alsa_pcm, logs); } ! bits_per_sample = snd_pcm_format_physical_width(outputf->format); ! alsa_bps = (outputf->rate * bits_per_sample * outputf->channels) >> 3; ! ! if (mmap) ! { ! int chn; ! buffer = g_malloc(alsa_period_size * bits_per_sample / 8 * outputf->channels); ! areas = g_malloc0(outputf->channels * sizeof(snd_pcm_channel_area_t)); ! ! for (chn = 0; chn < outputf->channels; chn++) ! { ! areas[chn].addr = buffer; ! areas[chn].first = chn * bits_per_sample; ! areas[chn].step = outputf->channels * bits_per_sample; ! } } debug("Device setup: buffer time: %i, size: %i.", alsa_buffer_time, ! snd_pcm_frames_to_bytes(alsa_pcm, alsa_buffer_size)); debug("bits per sample: %i; frame size: %i; Bps: %i", ! bits_per_sample, snd_pcm_frames_to_bytes(alsa_pcm, 1), alsa_bps); return 0; } --- 1069,1096 ---- snd_pcm_dump(alsa_pcm, logs); } ! hw_buffer_size = snd_pcm_frames_to_bytes(alsa_pcm, alsa_buffer_size); ! hw_period_size = snd_pcm_frames_to_bytes(alsa_pcm, alsa_period_size); ! if (inputf->bps != outputf->bps) { ! int align = (inputf->sample_bits * inputf->channels) / 8; ! hw_buffer_size_in = ((guint64)hw_buffer_size * inputf->bps + ! outputf->bps/2) / outputf->bps; ! hw_period_size_in = ((guint64)hw_period_size * inputf->bps + ! outputf->bps/2) / outputf->bps; ! hw_buffer_size_in -= hw_buffer_size_in % align; ! hw_period_size_in -= hw_period_size_in % align; ! } else { ! hw_buffer_size_in = hw_buffer_size; ! hw_period_size_in = hw_period_size; } debug("Device setup: buffer time: %i, size: %i.", alsa_buffer_time, ! hw_buffer_size); ! debug("Device setup: period time: %i, size: %i.", alsa_period_time, ! hw_period_size); debug("bits per sample: %i; frame size: %i; Bps: %i", ! snd_pcm_format_physical_width(outputf->format), ! snd_pcm_frames_to_bytes(alsa_pcm, 1), outputf->bps); return 0; } diff -rc xmms-1.2.10-orig/Output/alsa/configure.c xmms-1.2.10/Output/alsa/configure.c *** xmms-1.2.10-orig/Output/alsa/configure.c 2004-01-28 00:09:39.000000000 +0100 --- xmms-1.2.10/Output/alsa/configure.c 2006-01-27 00:28:49.000000000 +0100 *************** *** 20,27 **** #include static GtkWidget *configure_win = NULL; ! static GtkWidget *buffer_time_spin, *period_time_spin; ! static GtkWidget *mmap_button, *mixer_card_spin, *softvolume_toggle_button; static GtkWidget *devices_combo, *mixer_devices_combo; --- 20,27 ---- #include static GtkWidget *configure_win = NULL; ! static GtkWidget *buffer_time_spin, *period_time_spin, *thread_buffer_time_spin; ! static GtkWidget *mixer_card_spin, *softvolume_toggle_button; static GtkWidget *devices_combo, *mixer_devices_combo; *************** *** 36,42 **** alsa_cfg.pcm_device = GET_CHARS(GTK_COMBO(devices_combo)->entry); alsa_cfg.buffer_time = GET_SPIN_INT(buffer_time_spin); alsa_cfg.period_time = GET_SPIN_INT(period_time_spin); ! alsa_cfg.mmap = GET_TOGGLE(mmap_button); alsa_cfg.soft_volume = GET_TOGGLE(softvolume_toggle_button); alsa_cfg.mixer_card = GET_SPIN_INT(mixer_card_spin); alsa_cfg.mixer_device = GET_CHARS(GTK_COMBO(mixer_devices_combo)->entry); --- 36,42 ---- alsa_cfg.pcm_device = GET_CHARS(GTK_COMBO(devices_combo)->entry); alsa_cfg.buffer_time = GET_SPIN_INT(buffer_time_spin); alsa_cfg.period_time = GET_SPIN_INT(period_time_spin); ! alsa_cfg.thread_buffer_time = GET_SPIN_INT(thread_buffer_time_spin); alsa_cfg.soft_volume = GET_TOGGLE(softvolume_toggle_button); alsa_cfg.mixer_card = GET_SPIN_INT(mixer_card_spin); alsa_cfg.mixer_device = GET_CHARS(GTK_COMBO(mixer_devices_combo)->entry); *************** *** 51,57 **** xmms_cfg_write_int(cfgfile, "ALSA", "buffer_time", alsa_cfg.buffer_time); xmms_cfg_write_int(cfgfile, "ALSA", "period_time", alsa_cfg.period_time); ! xmms_cfg_write_boolean(cfgfile,"ALSA","mmap",alsa_cfg.mmap); xmms_cfg_write_string(cfgfile,"ALSA","pcm_device", alsa_cfg.pcm_device); xmms_cfg_write_int(cfgfile, "ALSA", "mixer_card", alsa_cfg.mixer_card); xmms_cfg_write_string(cfgfile,"ALSA","mixer_device", alsa_cfg.mixer_device); --- 51,57 ---- xmms_cfg_write_int(cfgfile, "ALSA", "buffer_time", alsa_cfg.buffer_time); xmms_cfg_write_int(cfgfile, "ALSA", "period_time", alsa_cfg.period_time); ! xmms_cfg_write_int(cfgfile, "ALSA", "thread_buffer_time", alsa_cfg.thread_buffer_time); xmms_cfg_write_string(cfgfile,"ALSA","pcm_device", alsa_cfg.pcm_device); xmms_cfg_write_int(cfgfile, "ALSA", "mixer_card", alsa_cfg.mixer_card); xmms_cfg_write_string(cfgfile,"ALSA","mixer_device", alsa_cfg.mixer_device); *************** *** 212,219 **** GtkWidget *dev_vbox, *adevice_frame, *adevice_box; GtkWidget *mixer_frame, *mixer_box, *mixer_card_box; GtkWidget *buffer_frame, *buffer_vbox, *buffer_table; ! GtkWidget *buffer_time_label, *period_time_label; ! GtkObject *buffer_time_adj, *period_time_adj, *mixer_card_adj; GtkWidget *bbox, *ok, *cancel; if (configure_win) --- 212,219 ---- GtkWidget *dev_vbox, *adevice_frame, *adevice_box; GtkWidget *mixer_frame, *mixer_box, *mixer_card_box; GtkWidget *buffer_frame, *buffer_vbox, *buffer_table; ! GtkWidget *buffer_time_label, *period_time_label, *thread_buffer_time_label; ! GtkObject *buffer_time_adj, *period_time_adj, *thread_buffer_time_adj, *mixer_card_adj; GtkWidget *bbox, *ok, *cancel; if (configure_win) *************** *** 312,318 **** gtk_container_set_border_width(GTK_CONTAINER(buffer_vbox), 5); ! buffer_table = gtk_table_new(2, 2, FALSE); gtk_table_set_row_spacings(GTK_TABLE(buffer_table), 5); gtk_table_set_col_spacings(GTK_TABLE(buffer_table), 5); gtk_box_pack_start(GTK_BOX(buffer_vbox), buffer_table, FALSE, FALSE, 0); --- 312,318 ---- gtk_container_set_border_width(GTK_CONTAINER(buffer_vbox), 5); ! buffer_table = gtk_table_new(2, 3, FALSE); gtk_table_set_row_spacings(GTK_TABLE(buffer_table), 5); gtk_table_set_col_spacings(GTK_TABLE(buffer_table), 5); gtk_box_pack_start(GTK_BOX(buffer_vbox), buffer_table, FALSE, FALSE, 0); *************** *** 345,354 **** gtk_table_attach(GTK_TABLE(buffer_table), period_time_spin, 1, 2, 1, 2, 0, 0, 0, 0); ! mmap_button = gtk_check_button_new_with_label(_("Mmap mode")); ! gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mmap_button), ! alsa_cfg.mmap); ! gtk_box_pack_start(GTK_BOX(buffer_vbox), mmap_button, FALSE, FALSE, 0); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), buffer_frame, gtk_label_new(_("Advanced settings"))); --- 345,363 ---- gtk_table_attach(GTK_TABLE(buffer_table), period_time_spin, 1, 2, 1, 2, 0, 0, 0, 0); ! thread_buffer_time_label = gtk_label_new(_("Thread buffer time (ms):")); ! gtk_label_set_justify(GTK_LABEL(thread_buffer_time_label), GTK_JUSTIFY_LEFT); ! gtk_misc_set_alignment(GTK_MISC(thread_buffer_time_label), 0, 0.5); ! gtk_table_attach(GTK_TABLE(buffer_table), thread_buffer_time_label, ! 0, 1, 2, 3, GTK_FILL, 0, 0, 0); ! thread_buffer_time_adj = gtk_adjustment_new(alsa_cfg.thread_buffer_time, ! 1000, 1000000, 100, 100, 100); ! thread_buffer_time_spin = gtk_spin_button_new(GTK_ADJUSTMENT(thread_buffer_time_adj), ! 8, 0); ! ! gtk_widget_set_usize(thread_buffer_time_spin, 60, -1); ! gtk_table_attach(GTK_TABLE(buffer_table), thread_buffer_time_spin, ! 1, 2, 2, 3, 0, 0, 0, 0); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), buffer_frame, gtk_label_new(_("Advanced settings"))); diff -rc xmms-1.2.10-orig/Output/alsa/init.c xmms-1.2.10/Output/alsa/init.c *** xmms-1.2.10-orig/Output/alsa/init.c 2004-01-11 17:27:26.000000000 +0100 --- xmms-1.2.10/Output/alsa/init.c 2006-01-27 00:28:49.000000000 +0100 *************** *** 29,36 **** memset(&alsa_cfg, 0, sizeof (alsa_cfg)); alsa_cfg.buffer_time = 500; alsa_cfg.period_time = 50; alsa_cfg.debug = 0; - alsa_cfg.mmap = 1; alsa_cfg.vol.left = 100; alsa_cfg.vol.right = 100; --- 29,36 ---- memset(&alsa_cfg, 0, sizeof (alsa_cfg)); alsa_cfg.buffer_time = 500; alsa_cfg.period_time = 50; + alsa_cfg.thread_buffer_time = 3000; alsa_cfg.debug = 0; alsa_cfg.vol.left = 100; alsa_cfg.vol.right = 100; *************** *** 44,51 **** xmms_cfg_read_int(cfgfile, "ALSA", "mixer_card", &alsa_cfg.mixer_card); xmms_cfg_read_int(cfgfile, "ALSA", "buffer_time", &alsa_cfg.buffer_time); xmms_cfg_read_int(cfgfile, "ALSA", "period_time", &alsa_cfg.period_time); ! xmms_cfg_read_boolean(cfgfile, "ALSA", "mmap", &alsa_cfg.mmap); ! xmms_cfg_read_int(cfgfile, "ALSA", "period_time", &alsa_cfg.period_time); xmms_cfg_read_boolean(cfgfile, "ALSA", "soft_volume", &alsa_cfg.soft_volume); xmms_cfg_read_int(cfgfile, "ALSA", "volume_left", &alsa_cfg.vol.left); --- 44,50 ---- xmms_cfg_read_int(cfgfile, "ALSA", "mixer_card", &alsa_cfg.mixer_card); xmms_cfg_read_int(cfgfile, "ALSA", "buffer_time", &alsa_cfg.buffer_time); xmms_cfg_read_int(cfgfile, "ALSA", "period_time", &alsa_cfg.period_time); ! xmms_cfg_read_int(cfgfile, "ALSA", "thread_buffer_time", &alsa_cfg.thread_buffer_time); xmms_cfg_read_boolean(cfgfile, "ALSA", "soft_volume", &alsa_cfg.soft_volume); xmms_cfg_read_int(cfgfile, "ALSA", "volume_left", &alsa_cfg.vol.left);