diff options
Diffstat (limited to 'shell/audio/alsa')
-rw-r--r-- | shell/audio/alsa/sound_output.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/shell/audio/alsa/sound_output.c b/shell/audio/alsa/sound_output.c new file mode 100644 index 0000000..5173bb6 --- /dev/null +++ b/shell/audio/alsa/sound_output.c @@ -0,0 +1,142 @@ +#include <sys/ioctl.h> +#include <stdint.h> +#include <fcntl.h> +#include <unistd.h> +#include <alsa/asoundlib.h> + +#include "sound_output.h" + +static snd_pcm_t *handle; + +uint32_t Audio_Init() +{ + snd_pcm_hw_params_t *params; + uint32_t val; + int32_t dir = -1; + snd_pcm_uframes_t frames; + + /* Open PCM device for playback. */ + int32_t rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); + + if (rc < 0) + rc = snd_pcm_open(&handle, "plughw:0,0,0", SND_PCM_STREAM_PLAYBACK, 0); + + if (rc < 0) + rc = snd_pcm_open(&handle, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0); + + if (rc < 0) + rc = snd_pcm_open(&handle, "plughw:1,0,0", SND_PCM_STREAM_PLAYBACK, 0); + + if (rc < 0) + rc = snd_pcm_open(&handle, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0); + + if (rc < 0) + { + fprintf(stderr, "unable to open PCM device: %s\n", snd_strerror(rc)); + return 1; + } + + /* Allocate a hardware parameters object. */ + snd_pcm_hw_params_alloca(¶ms); + + /* Fill it in with default values. */ + rc = snd_pcm_hw_params_any(handle, params); + if (rc < 0) + { + fprintf(stderr, "Error:snd_pcm_hw_params_any %s\n", snd_strerror(rc)); + return 1; + } + + /* Set the desired hardware parameters. */ + + /* Interleaved mode */ + rc = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (rc < 0) + { + fprintf(stderr, "Error:snd_pcm_hw_params_set_access %s\n", snd_strerror(rc)); + return 1; + } + + /* Signed 16-bit little-endian format */ + rc = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); + if (rc < 0) + { + fprintf(stderr, "Error:snd_pcm_hw_params_set_format %s\n", snd_strerror(rc)); + return 1; + } + + /* Two channels (stereo) */ + rc = snd_pcm_hw_params_set_channels(handle, params, 2); + if (rc < 0) + { + fprintf(stderr, "Error:snd_pcm_hw_params_set_channels %s\n", snd_strerror(rc)); + return 1; + } + + val = SOUND_OUTPUT_FREQUENCY; + rc = snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); + if (rc < 0) + { + fprintf(stderr, "Error:snd_pcm_hw_params_set_rate_near %s\n", snd_strerror(rc)); + return 1; + } + + /* Set period size to settings.aica.BufferSize frames. */ + frames = SOUND_SAMPLES_SIZE; + rc = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); + if (rc < 0) + { + fprintf(stderr, "Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); + return 1; + } + frames *= 4; + rc = snd_pcm_hw_params_set_buffer_size_near(handle, params, &frames); + if (rc < 0) + { + fprintf(stderr, "Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); + return 1; + } + + /* Write the parameters to the driver */ + rc = snd_pcm_hw_params(handle, params); + if (rc < 0) + { + fprintf(stderr, "Unable to set hw parameters: %s\n", snd_strerror(rc)); + return 1; + } + + return 0; +} + +void Audio_Write(int16_t* restrict buffer, uint32_t buffer_size) +{ + uint32_t i; + long ret, len; + + if (!handle) return; + + len = buffer_size; + + ret = snd_pcm_writei(handle, buffer, len); + while(ret != len) + { + if (ret < 0) + { + snd_pcm_prepare( handle ); + } + else + { + len -= ret; + } + ret = snd_pcm_writei(handle, buffer, len); + } +} + +void Audio_Close() +{ + if (handle) + { + snd_pcm_drain(handle); + snd_pcm_close(handle); + } +} |