summaryrefslogtreecommitdiff
path: root/dxr3audio-alsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'dxr3audio-alsa.c')
-rw-r--r--dxr3audio-alsa.c184
1 files changed, 184 insertions, 0 deletions
diff --git a/dxr3audio-alsa.c b/dxr3audio-alsa.c
new file mode 100644
index 0000000..c0350de
--- /dev/null
+++ b/dxr3audio-alsa.c
@@ -0,0 +1,184 @@
+/*
+ *
+ * Copyright (C) 2009 Christian Gmeiner
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <string>
+using namespace std;
+
+#include "dxr3audio-alsa.h"
+
+void cAudioAlsa::openDevice()
+{
+ string device = "default";
+ releaseDevice();
+
+ dsyslog("opening alsa device %s", device.c_str());
+ int err = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
+
+ if (err < 0) {
+ esyslog("[dxr3-audio-alsa] Playback open error: %s", snd_strerror(err));
+ exit(1);
+ }
+}
+
+void cAudioAlsa::releaseDevice()
+{
+ if (handle) {
+ snd_pcm_drain(handle);
+ snd_pcm_close(handle);
+ handle = NULL;
+ }
+}
+
+void cAudioAlsa::setup(SampleContext ctx)
+{
+ // look if ctx is different
+ if (curContext.channels == ctx.channels && curContext.samplerate == ctx.samplerate) {
+ return;
+ }
+
+ dsyslog("[dxr3-audio-alsa] changing samplerate to %d (old %d) ", ctx.samplerate, curContext.samplerate);
+ dsyslog("[dxr3-audio-alsa] changing num of channels to %d (old %d)", ctx.channels, curContext.channels);
+
+ snd_pcm_hw_params_t* alsa_hwparams;
+ snd_pcm_hw_params_alloca(&alsa_hwparams);
+
+ int err = snd_pcm_hw_params_any(handle, alsa_hwparams);
+ if (err < 0) {
+ esyslog("[dxr3-audio-alsa] Broken config for this PCM: no configurations available");
+ exit(1);
+ }
+
+ // set access type
+ err = snd_pcm_hw_params_set_access(handle, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (err < 0) {
+ esyslog("[dxr3-audio-alsa] Unable to set access type: %s", snd_strerror(err));
+ }
+
+ // set format
+ err = snd_pcm_hw_params_set_format(handle, alsa_hwparams, SND_PCM_FORMAT_S16_LE);
+ if (err < 0) {
+ esyslog("[dxr3-audio-alsa] Unable to set format: %s", snd_strerror(err));
+ }
+
+ // set channels
+ err = snd_pcm_hw_params_set_channels(handle, alsa_hwparams, ctx.channels);
+ if (err < 0) {
+ esyslog("[dxr3-audio-alsa] Unable to set channels %d: %s", ctx.channels, snd_strerror(err));
+ }
+
+ // set samplerate
+ err = snd_pcm_hw_params_set_rate_near(handle, alsa_hwparams, &ctx.samplerate, NULL);
+ if (err < 0) {
+ esyslog("[dxr3-audio-alsa] Unable to set samplerate %d: %s", ctx.samplerate, snd_strerror(err));
+ }
+
+ if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING) {
+ if ((err = snd_pcm_drain(handle)) < 0) {
+ esyslog("[dxr3-audio-alsa] Cannot drain (%s); will try to set parameters anyway\n", snd_strerror(err));
+ }
+ }
+
+ // set hardware pararmeters
+ err = snd_pcm_hw_params(handle, alsa_hwparams);
+ if (err < 0) {
+ esyslog("[dxr3-audio-alsa] Unable to set hw pararmeters: %s", snd_strerror(err));
+ }
+
+ // prepare for playback
+ err = snd_pcm_prepare(handle);
+ if (err < 0) {
+ esyslog("[dxr3-audio-alsa] Cannot prepare audio interface for use: %s", snd_strerror(err));
+ }
+
+ curContext.channels = ctx.channels;
+ curContext.samplerate = ctx.samplerate;
+
+ bytesFrame = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE) / 8;
+ bytesFrame *= curContext.channels;
+}
+
+void cAudioAlsa::write(uchar* data, size_t size)
+{
+ snd_pcm_uframes_t frames = size / bytesFrame;
+
+ if (frames == 0) {
+ dsyslog("[dxr3-audio-alsa] no frames");
+ return;
+ }
+
+ uchar *output_samples = data;
+ snd_pcm_sframes_t res = 0;
+
+ while (frames > 0) {
+ res = snd_pcm_writei(handle, output_samples, frames);
+
+ if (res == -EAGAIN) {
+ snd_pcm_wait(handle, 500);
+ } else if (res == -EINTR) {
+ // nothing to do
+ res = 0;
+ } else if (res == -EPIPE) {
+ Xrun();
+ } else if (res == -ESTRPIPE) {
+ // suspend
+ dsyslog("[dxr3-audio-alsa] pcm in suspend");
+ while ((res = snd_pcm_resume(handle)) == -EAGAIN) {
+ sleep(1);
+ }
+ }
+
+ if (res > 0) {
+ output_samples += res * bytesFrame;
+ frames -= res;
+ }
+ }
+}
+
+void cAudioAlsa::setAudioMode(AudioMode mode)
+{
+}
+
+void cAudioAlsa::Xrun()
+{
+ int res;
+ snd_pcm_status_alloca(&status);
+
+ dsyslog("[dxr3-audio-alsa] Xrun");
+
+ res = snd_pcm_status(handle, status);
+ if (res < 0) {
+ esyslog("[dxr3-audio-alsa]: Xrun status error: %s FATAL exiting", snd_strerror(res));
+ exit(EXIT_FAILURE);
+ }
+
+ if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
+
+ res = snd_pcm_prepare(handle);
+ if (res < 0) {
+ esyslog("[dxr3-audio-alsa]: Xrun prepare error: %s FATAL exiting", snd_strerror(res));
+ exit(EXIT_FAILURE);
+ }
+ return; // ok, data should be accepted again
+ }
+
+ esyslog("[dxr3-audio-alsa]: read/write error FATAL exiting");
+ exit(-1);
+}
+