/*---------------------------------------------------------------------- | AlsaOutput_Prepare +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Prepare(AlsaOutput* self) { int ior; switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: /* we need to be configured already for 'prepare' to work */ return BLT_FAILURE; case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* prepare the device */ ATX_LOG_FINER("snd_pcm_prepare"); ior = snd_pcm_prepare(self->device_handle); if (ior != 0) { ATX_LOG_FINER_2("snd_pcm_prepare() failed (%d : %s)", ior, snd_strerror(ior)); return BLT_FAILURE; } break; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* ignore */ return BLT_SUCCESS; } /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_PREPARED); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Close +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Close(AlsaOutput* self) { ATX_LOG_FINER("closing output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: /* ignore */ return BLT_SUCCESS; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* wait for buffers to finish */ ATX_LOG_FINER("snd_pcm_drain"); snd_pcm_drain(self->device_handle); /* FALLTHROUGH */ case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* close the device */ ATX_LOG_FINER("snd_pcm_close"); snd_pcm_close(self->device_handle); self->device_handle = NULL; break; } /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CLOSED); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Open +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Open(AlsaOutput* self) { int io_result; ATX_LOG_FINE_1("openning output - name=%s", ATX_CSTR(self->device_name)); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: ATX_LOG_FINER("snd_pcm_open"); io_result = snd_pcm_open(&self->device_handle, ATX_CSTR(self->device_name), SND_PCM_STREAM_PLAYBACK, 0); if (io_result != 0) { self->device_handle = NULL; return BLT_FAILURE; } break; case BLT_ALSA_OUTPUT_STATE_OPEN: /* ignore */ return BLT_SUCCESS; case BLT_ALSA_OUTPUT_STATE_CONFIGURED: case BLT_ALSA_OUTPUT_STATE_PREPARED: return BLT_FAILURE; } /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_OPEN); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Unprepare +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Unprepare(AlsaOutput* self) { BLT_Result result; ATX_LOG_FINER("unpreparing output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* ignore */ break; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* drain any pending samples */ result = AlsaOutput_Drain(self); if (BLT_FAILED(result)) return result; /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED); break; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Reset +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Reset(AlsaOutput* self) { ATX_LOG_FINER("resetting output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* ignore */ return BLT_SUCCESS; case BLT_ALSA_OUTPUT_STATE_PREPARED: ATX_LOG_FINER("snd_pcm_drop"); snd_pcm_drop(self->device_handle); AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED); break; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Unprepare +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Unprepare(AlsaOutput* self) { ATX_LOG_FINER("unpreparing output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* ignore */ break; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* drain any pending samples */ ATX_LOG_FINER("snd_pcm_drain"); snd_pcm_drain(self->device_handle); /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED); break; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Configure +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Configure(AlsaOutput* self, const BLT_PcmMediaType* format) { snd_pcm_hw_params_t* hw_params; snd_pcm_sw_params_t* sw_params; unsigned int rate = format->sample_rate; unsigned int buffer_time = BLT_ALSA_DEFAULT_BUFFER_TIME; snd_pcm_uframes_t buffer_size = 0; snd_pcm_uframes_t period_size = BLT_ALSA_DEFAULT_PERIOD_SIZE; snd_pcm_format_t pcm_format_id = SND_PCM_FORMAT_UNKNOWN; int ior; BLT_Result result; switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: /* first, we need to open the device */ result = AlsaOutput_Open(self); if (BLT_FAILED(result)) return result; /* FALLTHROUGH */ case BLT_ALSA_OUTPUT_STATE_CONFIGURED: case BLT_ALSA_OUTPUT_STATE_PREPARED: /* check to see if the format has changed */ if (format->sample_rate != self->media_type.sample_rate || format->channel_count != self->media_type.channel_count || format->bits_per_sample != self->media_type.bits_per_sample) { /* new format */ /* check the format */ if (format->sample_rate == 0 || format->channel_count == 0 || format->bits_per_sample == 0) { return BLT_ERROR_INVALID_MEDIA_FORMAT; } /* unprepare (forget current settings) */ result = AlsaOutput_Unprepare(self); if (BLT_FAILED(result)) return result; } else { /* same format, do nothing */ return BLT_SUCCESS; } /* FALLTHROUGH */ case BLT_ALSA_OUTPUT_STATE_OPEN: /* configure the device with the new format */ ATX_LOG_FINER("configuring ALSA device"); /* copy the format */ self->media_type = *format; ATX_LOG_FINE_3("new format: sr=%d, ch=%d, bps=%d", format->sample_rate, format->channel_count, format->bits_per_sample); /* allocate a new blank configuration */ snd_pcm_hw_params_alloca_no_assert(&hw_params); snd_pcm_hw_params_any(self->device_handle, hw_params); /* use interleaved access */ ior = snd_pcm_hw_params_set_access(self->device_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params_set_access failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the sample rate */ ior = snd_pcm_hw_params_set_rate_near(self->device_handle, hw_params, &rate, NULL); if (ior != 0) { ATX_LOG_WARNING_3("snd_pcm_hw_params_set_rate_near(%d) failed (%d:%s)", rate, ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the number of channels */ ior = snd_pcm_hw_params_set_channels(self->device_handle, hw_params, format->channel_count); if (ior != 0) { ATX_LOG_WARNING_3("snd_pcm_hw_params_set_channels(%d) failed (%d:%s)", format->channel_count, ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the sample format */ switch (format->sample_format) { case BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_LE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_LE"); switch (format->bits_per_sample) { case 8: pcm_format_id = SND_PCM_FORMAT_S8; break; case 16: pcm_format_id = SND_PCM_FORMAT_S16_LE; break; case 24: pcm_format_id = SND_PCM_FORMAT_S24_3LE; break; case 32: pcm_format_id = SND_PCM_FORMAT_S32_LE; break; } break; case BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_LE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_LE"); switch (format->bits_per_sample) { case 8: pcm_format_id = SND_PCM_FORMAT_U8; break; case 16: pcm_format_id = SND_PCM_FORMAT_U16_LE; break; case 24: pcm_format_id = SND_PCM_FORMAT_U24_3LE; break; case 32: pcm_format_id = SND_PCM_FORMAT_U32_LE; break; } break; case BLT_PCM_SAMPLE_FORMAT_FLOAT_LE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_FLOAT_LE"); switch (format->bits_per_sample) { case 32: pcm_format_id = SND_PCM_FORMAT_FLOAT_LE; break; } break; case BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_BE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_BE"); switch (format->bits_per_sample) { case 8: pcm_format_id = SND_PCM_FORMAT_S8; break; case 16: pcm_format_id = SND_PCM_FORMAT_S16_BE; break; case 24: pcm_format_id = SND_PCM_FORMAT_S24_3BE; break; case 32: pcm_format_id = SND_PCM_FORMAT_S32_BE; break; } break; case BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_BE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_BE"); switch (format->bits_per_sample) { case 8: pcm_format_id = SND_PCM_FORMAT_U8; break; case 16: pcm_format_id = SND_PCM_FORMAT_U16_BE; break; case 24: pcm_format_id = SND_PCM_FORMAT_U24_3BE; break; case 32: pcm_format_id = SND_PCM_FORMAT_U32_BE; break; } break; case BLT_PCM_SAMPLE_FORMAT_FLOAT_BE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_FLOAT_LE"); switch (format->bits_per_sample) { case 32: pcm_format_id = SND_PCM_FORMAT_FLOAT_BE; break; } break; } if (pcm_format_id == SND_PCM_FORMAT_UNKNOWN) { return BLT_ERROR_INVALID_MEDIA_TYPE; } ior = snd_pcm_hw_params_set_format(self->device_handle, hw_params, pcm_format_id); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params_set_format() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the period size */ ior = snd_pcm_hw_params_set_period_size_near(self->device_handle, hw_params, &period_size, NULL); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params_set_period_size_near() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the buffer time (duration) */ ior = snd_pcm_hw_params_set_buffer_time_near(self->device_handle, hw_params, &buffer_time, NULL); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params_set_buffer_time_near() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* get the actual buffer size */ snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size); /* activate this configuration */ ior = snd_pcm_hw_params(self->device_handle, hw_params); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* configure the software parameters */ snd_pcm_sw_params_alloca_no_assert(&sw_params); snd_pcm_sw_params_current(self->device_handle, sw_params); /* set the start threshold to 1/2 the buffer size */ snd_pcm_sw_params_set_start_threshold(self->device_handle, sw_params, buffer_size/2); /* set the buffer alignment */ /* NOTE: this call is now obsolete */ /* snd_pcm_sw_params_set_xfer_align(self->device_handle, sw_params, 1); */ /* activate the sofware parameters */ ior = snd_pcm_sw_params(self->device_handle, sw_params); if (ior != 0) { ATX_LOG_SEVERE_2("snd_pcm_sw_params() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* print status info */ { snd_pcm_uframes_t val; ATX_LOG_FINER_1("sample type = %x", pcm_format_id); if (rate != format->sample_rate) { ATX_LOG_FINER_1("actual sample = %d", rate); } ATX_LOG_FINER_1("actual buffer time = %d", (int)buffer_time); ATX_LOG_FINER_1("buffer size = %d", (int)buffer_size); snd_pcm_sw_params_get_start_threshold(sw_params, &val); ATX_LOG_FINER_1("start threshold = %d", (int)val); snd_pcm_sw_params_get_stop_threshold(sw_params, &val); ATX_LOG_FINER_1("stop threshold = %d", (int)val); snd_pcm_hw_params_get_period_size(hw_params, &val, NULL); ATX_LOG_FINER_1("period size = %d", (int)val); } break; } /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED); return BLT_SUCCESS; }