| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <sched.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <getopt.h>
 | 
					
						
							|  |  |  | #include <sys/time.h>
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  | #include <lib/debug.h>
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | #include "alsa-utils.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  | #define CHECK(s,msg) if ((err = (s)) < 0) { spa_log_error (state->log, msg ": %s", snd_strerror(err)); return err; }
 | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  | static int alsa_on_fd_events (SpaPollNotifyData *data); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | spa_alsa_open (SpaALSAState *state) | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   int err; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   SpaALSAProps *props = &state->props[1]; | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (state->opened) | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-12 15:25:53 +02:00
										 |  |  |   CHECK (snd_output_stdio_attach (&state->output, stderr, 0), "attach failed"); | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |   spa_log_info (state->log, "ALSA device open '%s'", props->device); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_open (&state->hndl, | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  |                        props->device, | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |                        state->stream, | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  |                        SND_PCM_NONBLOCK | | 
					
						
							|  |  |  |                        SND_PCM_NO_AUTO_RESAMPLE | | 
					
						
							|  |  |  |                        SND_PCM_NO_AUTO_CHANNELS | | 
					
						
							|  |  |  |                        SND_PCM_NO_AUTO_FORMAT), "open failed"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   state->poll.id = 0; | 
					
						
							|  |  |  |   state->poll.enabled = false; | 
					
						
							|  |  |  |   state->poll.fds = state->fds; | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   state->poll.n_fds = 0; | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   state->poll.idle_cb = NULL; | 
					
						
							|  |  |  |   state->poll.before_cb = NULL; | 
					
						
							|  |  |  |   state->poll.after_cb = alsa_on_fd_events; | 
					
						
							|  |  |  |   state->poll.user_data = state; | 
					
						
							|  |  |  |   spa_poll_add_item (state->data_loop, &state->poll); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  |   state->opened = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  | int | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | spa_alsa_close (SpaALSAState *state) | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!state->opened) | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   spa_poll_remove_item (state->data_loop, &state->poll); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |   spa_log_info (state->log, "Device closing"); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_close (state->hndl), "close failed"); | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   state->opened = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | static snd_pcm_format_t | 
					
						
							|  |  |  | spa_alsa_format_to_alsa (SpaAudioFormat format) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   switch (format) { | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S8: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S8; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U8: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U8; | 
					
						
							|  |  |  |     /* 16 bit */ | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S16LE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S16_LE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S16BE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S16_BE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U16LE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U16_LE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U16BE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U16_BE; | 
					
						
							|  |  |  |     /* 24 bit in low 3 bytes of 32 bits */ | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S24_32LE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S24_LE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S24_32BE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S24_BE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U24_32LE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U24_LE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U24_32BE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U24_BE; | 
					
						
							|  |  |  |     /* 24 bit in 3 bytes */ | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S24LE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S24_3LE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S24BE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S24_3BE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U24LE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U24_3LE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U24BE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U24_3BE; | 
					
						
							|  |  |  |     /* 32 bit */ | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S32LE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S32_LE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_S32BE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_S32_BE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U32LE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U32_LE; | 
					
						
							|  |  |  |     case SPA_AUDIO_FORMAT_U32BE: | 
					
						
							|  |  |  |       return SND_PCM_FORMAT_U32_BE; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SND_PCM_FORMAT_UNKNOWN; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | int | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  | spa_alsa_set_format (SpaALSAState *state, SpaFormatAudio *fmt, SpaPortFormatFlags flags) | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  |   unsigned int rrate, rchannels; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   snd_pcm_uframes_t size; | 
					
						
							|  |  |  |   int err, dir; | 
					
						
							|  |  |  |   snd_pcm_hw_params_t *params; | 
					
						
							|  |  |  |   snd_pcm_format_t format; | 
					
						
							| 
									
										
										
										
											2016-08-26 19:22:50 +02:00
										 |  |  |   SpaAudioInfoRaw *info = &fmt->info.raw; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   snd_pcm_t *hndl; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   unsigned int buffer_time; | 
					
						
							|  |  |  |   unsigned int period_time; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   SpaALSAProps *props = &state->props[1]; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   if ((err = spa_alsa_open (state)) < 0) | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  |     return err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   hndl = state->hndl; | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   snd_pcm_hw_params_alloca (¶ms); | 
					
						
							|  |  |  |   /* choose all parameters */ | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_any (hndl, params), "Broken configuration for playback: no configurations available"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   /* set hardware resampling */ | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_set_rate_resample (hndl, params, 0), "set_rate_resample"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   /* set the interleaved read/write format */ | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_set_access(hndl, params, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set_access"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* set the sample format */ | 
					
						
							|  |  |  |   format = spa_alsa_format_to_alsa (info->format); | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |   spa_log_info (state->log, "Stream parameters are %iHz, %s, %i channels", info->rate, snd_pcm_format_name(format), info->channels); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_set_format (hndl, params, format), "set_format"); | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   /* set the count of channels */ | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  |   rchannels = info->channels; | 
					
						
							|  |  |  |   CHECK (snd_pcm_hw_params_set_channels_near (hndl, params, &rchannels), "set_channels"); | 
					
						
							|  |  |  |   if (rchannels != info->channels) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_info (state->log, "Channels doesn't match (requested %u, get %u", info->channels, rchannels); | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  |     if (flags & SPA_PORT_FORMAT_FLAG_NEAREST) | 
					
						
							|  |  |  |       info->channels = rchannels; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       return -EINVAL; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   /* set the stream rate */ | 
					
						
							|  |  |  |   rrate = info->rate; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_set_rate_near (hndl, params, &rrate, 0), "set_rate_near"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   if (rrate != info->rate) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_info (state->log, "Rate doesn't match (requested %iHz, get %iHz)", info->rate, rrate); | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  |     if (flags & SPA_PORT_FORMAT_FLAG_NEAREST) | 
					
						
							|  |  |  |       info->rate = rrate; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       return -EINVAL; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  |   state->format = format; | 
					
						
							|  |  |  |   state->channels = info->channels; | 
					
						
							|  |  |  |   state->rate = info->rate; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   state->frame_size = info->channels * 2; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   /* set the buffer time */ | 
					
						
							| 
									
										
										
										
											2016-07-07 09:30:18 +02:00
										 |  |  |   buffer_time = props->buffer_time; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_set_buffer_time_near (hndl, params, &buffer_time, &dir), "set_buffer_time_near"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_get_buffer_size (params, &size), "get_buffer_size"); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   state->buffer_frames = size; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* set the period time */ | 
					
						
							| 
									
										
										
										
											2016-07-07 09:30:18 +02:00
										 |  |  |   period_time = props->period_time; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_set_period_time_near (hndl, params, &period_time, &dir), "set_period_time_near"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params_get_period_size (params, &size, &dir), "get_period_size"); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   state->period_frames = size; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |   spa_log_info (state->log, "buffer frames %zd, period frames %zd", state->buffer_frames, state->period_frames); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   /* write the parameters to device */ | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_hw_params (hndl, params), "set_hw_params"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | set_swparams (SpaALSAState *state) | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   snd_pcm_t *hndl = state->hndl; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   int err = 0; | 
					
						
							|  |  |  |   snd_pcm_sw_params_t *params; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   SpaALSAProps *props = &state->props[1]; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   snd_pcm_sw_params_alloca (¶ms); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* get the current params */ | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_sw_params_current (hndl, params), "sw_params_current"); | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   CHECK (snd_pcm_sw_params_set_tstamp_mode (hndl, params, SND_PCM_TSTAMP_ENABLE), "sw_params_set_tstamp_mode"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |   /* start the transfer */ | 
					
						
							|  |  |  |   CHECK (snd_pcm_sw_params_set_start_threshold (hndl, params, 0U), "set_start_threshold"); | 
					
						
							|  |  |  |   CHECK (snd_pcm_sw_params_set_stop_threshold (hndl, params, | 
					
						
							|  |  |  |          (state->buffer_frames / state->period_frames) * state->period_frames), "set_stop_threshold"); | 
					
						
							|  |  |  | //  CHECK (snd_pcm_sw_params_set_stop_threshold (hndl, params, -1), "set_stop_threshold");
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   CHECK (snd_pcm_sw_params_set_silence_threshold (hndl, params, 0U), "set_silence_threshold"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* allow the transfer when at least period_size samples can be processed */ | 
					
						
							|  |  |  |   /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */ | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_sw_params_set_avail_min (hndl, params, | 
					
						
							|  |  |  |         props->period_event ? state->buffer_frames : state->period_frames), "set_avail_min"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   /* enable period events when requested */ | 
					
						
							| 
									
										
										
										
											2016-07-07 09:30:18 +02:00
										 |  |  |   if (props->period_event) { | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |     CHECK (snd_pcm_sw_params_set_period_event (hndl, params, 1), "set_period_event"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   } | 
					
						
							|  |  |  |   /* write the parameters to the playback device */ | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (snd_pcm_sw_params (hndl, params), "sw_params"); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *   Underrun and suspend recovery | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2016-10-07 13:14:32 +02:00
										 |  |  | xrun_recovery (SpaALSAState *state, snd_pcm_t *hndl, int err) | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   snd_pcm_status_t *status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   snd_pcm_status_alloca (&status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ((err = snd_pcm_status (hndl, status)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_error (state->log, "snd_pcm_status error: %s", snd_strerror (err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (snd_pcm_status_get_state (status) == SND_PCM_STATE_SUSPENDED) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_warn (state->log, "SUSPENDED, trying to resume"); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((err = snd_pcm_prepare (hndl)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |       spa_log_error (state->log, "snd_pcm_prepare error: %s", snd_strerror (err)); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_warn (state->log, "XRUN"); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   if (spa_alsa_pause (state, true) != SPA_RESULT_OK) | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  |   if (spa_alsa_start (state, true) != SPA_RESULT_OK) | 
					
						
							|  |  |  |     return -1; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   return err; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  | static snd_pcm_uframes_t | 
					
						
							|  |  |  | pull_frames_queue (SpaALSAState *state, | 
					
						
							|  |  |  |                    const snd_pcm_channel_area_t *my_areas, | 
					
						
							|  |  |  |                    snd_pcm_uframes_t offset, | 
					
						
							|  |  |  |                    snd_pcm_uframes_t frames) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SpaALSABuffer *b; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SPA_QUEUE_PEEK_HEAD (&state->ready, SpaALSABuffer, b); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (b) { | 
					
						
							|  |  |  |     uint8_t *src, *dst; | 
					
						
							|  |  |  |     size_t n_bytes; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     src = SPA_MEMBER (b->outbuf->datas[0].data, b->outbuf->datas[0].offset + state->ready_offset, uint8_t); | 
					
						
							|  |  |  |     dst = SPA_MEMBER (my_areas[0].addr, offset * state->frame_size, uint8_t); | 
					
						
							|  |  |  |     n_bytes = SPA_MIN (b->outbuf->datas[0].size - state->ready_offset, frames * state->frame_size); | 
					
						
							|  |  |  |     frames = SPA_MIN (frames, n_bytes / state->frame_size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memcpy (dst, src, n_bytes); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     state->ready_offset += n_bytes; | 
					
						
							|  |  |  |     if (state->ready_offset >= b->outbuf->datas[0].size) { | 
					
						
							|  |  |  |       SpaNodeEventReuseBuffer rb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       SPA_QUEUE_POP_HEAD (&state->ready, SpaALSABuffer, next, b); | 
					
						
							|  |  |  |       b->outstanding = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       rb.event.type = SPA_NODE_EVENT_TYPE_REUSE_BUFFER; | 
					
						
							|  |  |  |       rb.event.size = sizeof (rb); | 
					
						
							|  |  |  |       rb.port_id = 0; | 
					
						
							|  |  |  |       rb.buffer_id = b->outbuf->id; | 
					
						
							|  |  |  |       state->event_cb (&state->node, &rb.event, state->user_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       state->ready_offset = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     spa_log_warn (state->log, "underrun"); | 
					
						
							|  |  |  |     snd_pcm_areas_silence (my_areas, offset, state->channels, frames, state->format); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return frames; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static snd_pcm_uframes_t | 
					
						
							|  |  |  | pull_frames_ringbuffer (SpaALSAState *state, | 
					
						
							|  |  |  |                         const snd_pcm_channel_area_t *my_areas, | 
					
						
							|  |  |  |                         snd_pcm_uframes_t offset, | 
					
						
							|  |  |  |                         snd_pcm_uframes_t frames) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SpaRingbufferArea areas[2]; | 
					
						
							|  |  |  |   size_t size, avail; | 
					
						
							|  |  |  |   SpaALSABuffer *b; | 
					
						
							|  |  |  |   uint8_t *src, *dst; | 
					
						
							|  |  |  |   SpaNodeEventReuseBuffer rb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   b = state->ringbuffer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   src = SPA_MEMBER (b->outbuf->datas[0].data, b->outbuf->datas[0].offset, void); | 
					
						
							|  |  |  |   dst = SPA_MEMBER (my_areas[0].addr, offset * state->frame_size, uint8_t); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   spa_ringbuffer_get_read_areas (&b->rb->ringbuffer, areas); | 
					
						
							|  |  |  |   avail = areas[0].len + areas[1].len; | 
					
						
							|  |  |  |   size = SPA_MIN (avail, frames * state->frame_size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   spa_log_debug (state->log, "%zd %zd %zd %zd %zd %zd", | 
					
						
							|  |  |  |       areas[0].offset, areas[0].len, | 
					
						
							|  |  |  |       areas[1].offset, areas[1].len, offset, size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (size > 0) { | 
					
						
							|  |  |  |     areas[0].len = SPA_MIN (areas[0].len, size); | 
					
						
							|  |  |  |     areas[1].len = SPA_MIN (areas[1].len, size - areas[0].len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memcpy (dst, src + areas[0].offset, areas[0].len); | 
					
						
							|  |  |  |     if (areas[1].len) | 
					
						
							|  |  |  |       memcpy (dst + areas[0].len, src + areas[1].offset, areas[1].len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     spa_ringbuffer_read_advance (&b->rb->ringbuffer, size); | 
					
						
							|  |  |  |     frames = size / state->frame_size; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     spa_log_warn (state->log, "underrun"); | 
					
						
							|  |  |  |     snd_pcm_areas_silence (my_areas, offset, state->channels, frames, state->format); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   b->outstanding = true; | 
					
						
							|  |  |  |   rb.event.type = SPA_NODE_EVENT_TYPE_REUSE_BUFFER; | 
					
						
							|  |  |  |   rb.event.size = sizeof (rb); | 
					
						
							|  |  |  |   rb.port_id = 0; | 
					
						
							|  |  |  |   rb.buffer_id = b->outbuf->id; | 
					
						
							|  |  |  |   state->event_cb (&state->node, &rb.event, state->user_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return frames; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | mmap_write (SpaALSAState *state) | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   snd_pcm_t *hndl = state->hndl; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |   int err; | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   snd_pcm_sframes_t avail; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |   snd_pcm_uframes_t offset, frames, size; | 
					
						
							|  |  |  |   const snd_pcm_channel_area_t *my_areas; | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   snd_pcm_status_t *status; | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |   SpaNodeEventNeedInput ni; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   snd_pcm_status_alloca (&status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ((err = snd_pcm_status (hndl, status)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_error (state->log, "snd_pcm_status error: %s", snd_strerror (err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |     return -1; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   avail = snd_pcm_status_get_avail (status); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |   ni.event.type = SPA_NODE_EVENT_TYPE_NEED_INPUT; | 
					
						
							|  |  |  |   ni.event.size = sizeof (ni); | 
					
						
							|  |  |  |   ni.port_id = 0; | 
					
						
							|  |  |  |   state->event_cb (&state->node, &ni.event, state->user_data); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |   size = avail; | 
					
						
							|  |  |  |   while (size > 0) { | 
					
						
							|  |  |  |     frames = size; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |     if ((err = snd_pcm_mmap_begin (hndl, &my_areas, &offset, &frames)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |       spa_log_error (state->log, "snd_pcm_mmap_begin error: %s", snd_strerror(err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |       return -1; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     if (state->ringbuffer) | 
					
						
							|  |  |  |       frames = pull_frames_ringbuffer (state, my_areas, offset, frames); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       frames = pull_frames_queue (state, my_areas, offset, frames); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((err = snd_pcm_mmap_commit (hndl, offset, frames)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |       spa_log_error (state->log, "snd_pcm_mmap_commit error: %s", snd_strerror(err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |       if (err != -EPIPE && err != -ESTRPIPE) | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_debug (state->log, "write %zd/%zd/%zd %u", frames, size, avail, state->ready.length); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |     size -= frames; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | static int | 
					
						
							|  |  |  | mmap_read (SpaALSAState *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   snd_pcm_t *hndl = state->hndl; | 
					
						
							|  |  |  |   int err; | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   snd_pcm_sframes_t avail; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   snd_pcm_uframes_t offset, frames, size; | 
					
						
							|  |  |  |   snd_pcm_status_t *status; | 
					
						
							|  |  |  |   const snd_pcm_channel_area_t *my_areas; | 
					
						
							|  |  |  |   SpaALSABuffer *b; | 
					
						
							|  |  |  |   snd_htimestamp_t htstamp = { 0, 0 }; | 
					
						
							|  |  |  |   int64_t now; | 
					
						
							| 
									
										
										
										
											2016-09-29 18:18:59 +02:00
										 |  |  |   uint8_t *dest = NULL; | 
					
						
							| 
									
										
										
										
											2016-10-04 19:37:44 +02:00
										 |  |  |   size_t destsize; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   snd_pcm_status_alloca(&status); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   if ((err = snd_pcm_status (hndl, status)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_error (state->log, "snd_pcm_status error: %s", snd_strerror(err)); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |     return err; | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   avail = snd_pcm_status_get_avail (status); | 
					
						
							|  |  |  |   snd_pcm_status_get_htstamp (status, &htstamp); | 
					
						
							| 
									
										
										
										
											2016-09-20 19:52:05 +02:00
										 |  |  |   now = (int64_t)htstamp.tv_sec * SPA_NSEC_PER_SEC + (int64_t)htstamp.tv_nsec; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   state->last_ticks = state->sample_count * SPA_USEC_PER_SEC / state->rate; | 
					
						
							|  |  |  |   state->last_monotonic = now; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 19:17:59 +02:00
										 |  |  |   SPA_QUEUE_POP_HEAD (&state->free, SpaALSABuffer, next, b); | 
					
						
							| 
									
										
										
										
											2016-09-19 13:14:14 +02:00
										 |  |  |   if (b == NULL) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_warn (state->log, "no more buffers"); | 
					
						
							| 
									
										
										
										
											2016-09-19 13:14:14 +02:00
										 |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2016-10-04 19:37:44 +02:00
										 |  |  |     dest = SPA_MEMBER (b->outbuf->datas[0].data, b->outbuf->datas[0].offset, void); | 
					
						
							|  |  |  |     destsize = b->outbuf->datas[0].size; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-20 19:52:05 +02:00
										 |  |  |     if (b->h) { | 
					
						
							|  |  |  |       b->h->seq = state->sample_count; | 
					
						
							|  |  |  |       b->h->pts = state->last_monotonic; | 
					
						
							|  |  |  |       b->h->dts_offset = 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-10-04 19:37:44 +02:00
										 |  |  |     avail = SPA_MIN (avail, destsize / state->frame_size); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-20 19:52:05 +02:00
										 |  |  |   state->sample_count += avail; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   size = avail; | 
					
						
							|  |  |  |   while (size > 0) { | 
					
						
							|  |  |  |     frames = size; | 
					
						
							|  |  |  |     if ((err = snd_pcm_mmap_begin (hndl, &my_areas, &offset, &frames)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |       spa_log_error (state->log, "snd_pcm_mmap_begin error: %s", snd_strerror (err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |       return -1; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (b) { | 
					
						
							|  |  |  |       size_t n_bytes = frames * state->frame_size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       memcpy (dest, | 
					
						
							|  |  |  |               (uint8_t *)my_areas[0].addr + (offset * state->frame_size), | 
					
						
							|  |  |  |               n_bytes); | 
					
						
							|  |  |  |       dest += n_bytes; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |     if ((err = snd_pcm_mmap_commit (hndl, offset, frames)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |       spa_log_error (state->log, "snd_pcm_mmap_commit error: %s", snd_strerror(err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |       return -1; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     size -= frames; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (b) { | 
					
						
							|  |  |  |     SpaNodeEventHaveOutput ho; | 
					
						
							|  |  |  |     SpaData *d; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-29 18:18:59 +02:00
										 |  |  |     d = b->outbuf->datas; | 
					
						
							|  |  |  |     d[0].size = avail * state->frame_size; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-20 11:20:43 +02:00
										 |  |  |     b->next = NULL; | 
					
						
							| 
									
										
										
										
											2016-09-19 19:17:59 +02:00
										 |  |  |     SPA_QUEUE_PUSH_TAIL (&state->ready, SpaALSABuffer, next, b); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-24 15:30:15 +02:00
										 |  |  |     ho.event.type = SPA_NODE_EVENT_TYPE_HAVE_OUTPUT; | 
					
						
							|  |  |  |     ho.event.size = sizeof (ho); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |     ho.port_id = 0; | 
					
						
							| 
									
										
										
										
											2016-10-24 15:30:15 +02:00
										 |  |  |     state->event_cb (&state->node, &ho.event, state->user_data); | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | alsa_on_fd_events (SpaPollNotifyData *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   SpaALSAState *state = data->user_data; | 
					
						
							|  |  |  |   snd_pcm_t *hndl = state->hndl; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |   int err; | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   unsigned short revents = 0; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   snd_pcm_poll_descriptors_revents (hndl, | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |                                     (struct pollfd *)data->fds, | 
					
						
							|  |  |  |                                     data->n_fds, | 
					
						
							|  |  |  |                                     &revents); | 
					
						
							|  |  |  |   if (revents & POLLERR) { | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |     if ((err = xrun_recovery (state, hndl, err)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |       spa_log_error (state->log, "error: %s", snd_strerror (err)); | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  |       return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   if (state->stream == SND_PCM_STREAM_CAPTURE) { | 
					
						
							|  |  |  |     if (!(revents & POLLIN)) | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |       return 0; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     mmap_read (state); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     if (!(revents & POLLOUT)) | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |       return 0; | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     mmap_write (state); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  | SpaResult | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  | spa_alsa_start (SpaALSAState *state, bool xrun_recover) | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   int err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   if (state->started) | 
					
						
							|  |  |  |     return SPA_RESULT_OK; | 
					
						
							| 
									
										
										
										
											2016-08-18 12:43:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-19 09:16:58 +02:00
										 |  |  |   CHECK (set_swparams (state), "swparams"); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   if (!xrun_recover) | 
					
						
							|  |  |  |     snd_pcm_dump (state->hndl, state->output); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ((err = snd_pcm_prepare (state->hndl)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_error (state->log, "snd_pcm_prepare error: %s", snd_strerror (err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |     return SPA_RESULT_ERROR; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ((state->poll.n_fds = snd_pcm_poll_descriptors_count (state->hndl)) <= 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_error (state->log, "Invalid poll descriptors count %d", state->poll.n_fds); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |     return SPA_RESULT_ERROR; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if ((err = snd_pcm_poll_descriptors (state->hndl, (struct pollfd *)state->fds, state->poll.n_fds)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_error (state->log, "snd_pcm_poll_descriptors: %s", snd_strerror(err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |     return SPA_RESULT_ERROR; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!xrun_recover) { | 
					
						
							|  |  |  |     state->poll.enabled = true; | 
					
						
							|  |  |  |     spa_poll_update_item (state->data_loop, &state->poll); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   if (state->stream == SND_PCM_STREAM_PLAYBACK) { | 
					
						
							|  |  |  |     mmap_write (state); | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   if ((err = snd_pcm_start (state->hndl)) < 0) { | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_error (state->log, "snd_pcm_start: %s", snd_strerror (err)); | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |     return SPA_RESULT_ERROR; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   state->started = true; | 
					
						
							| 
									
										
										
										
											2016-07-08 12:18:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   return SPA_RESULT_OK; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  | SpaResult | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  | spa_alsa_pause (SpaALSAState *state, bool xrun_recover) | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   int err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   if (!state->started) | 
					
						
							|  |  |  |     return SPA_RESULT_OK; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 16:56:33 +02:00
										 |  |  |   if (!xrun_recover) { | 
					
						
							|  |  |  |     state->poll.enabled = false; | 
					
						
							|  |  |  |     spa_poll_update_item (state->data_loop, &state->poll); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ((err = snd_pcm_drop (state->hndl)) < 0) | 
					
						
							| 
									
										
										
										
											2016-11-03 19:41:53 +01:00
										 |  |  |     spa_log_error (state->log, "snd_pcm_drop %s", snd_strerror (err)); | 
					
						
							| 
									
										
										
										
											2016-08-24 16:26:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   state->started = false; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-25 11:15:15 +02:00
										 |  |  |   return SPA_RESULT_OK; | 
					
						
							| 
									
										
										
										
											2016-06-28 12:21:56 +02:00
										 |  |  | } |