summaryrefslogtreecommitdiffstats
path: root/radio_output.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'radio_output.cpp')
-rw-r--r--radio_output.cpp152
1 files changed, 152 insertions, 0 deletions
diff --git a/radio_output.cpp b/radio_output.cpp
new file mode 100644
index 0000000..350bf75
--- /dev/null
+++ b/radio_output.cpp
@@ -0,0 +1,152 @@
+/*
+ * A standalone AM/FM Radio QML plugin (for RTL2832U and Maxim hardware)
+ * Copyright © 2015-2016 Manuel Bachmann <manuel.bachmann@iot.bzh>
+ * Copyright © 2016 Scott Murray <scott.murray@konsulko.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include "radio_output.h"
+#include "rtl_fm.h"
+
+RadioOutputAlsa::RadioOutputAlsa() : RadioOutputImplementation(),
+ dev(NULL),
+ hw_params(NULL)
+{
+ unsigned int rate = 24000;
+
+ if (snd_pcm_open(&dev, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) {
+ std::cerr << "Could not open primary ALSA device" << std::endl;
+ works = false;
+ return;
+ }
+
+ snd_pcm_hw_params_malloc(&hw_params);
+ snd_pcm_hw_params_any(dev, hw_params);
+
+ snd_pcm_hw_params_set_access (dev, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
+ snd_pcm_hw_params_set_format (dev, hw_params, SND_PCM_FORMAT_S16_LE);
+ snd_pcm_hw_params_set_rate_near (dev, hw_params, &rate, 0);
+ snd_pcm_hw_params_set_channels (dev, hw_params, 2);
+
+ if (snd_pcm_hw_params (dev, hw_params) < 0) {
+ std::cerr << "Could not set hardware parameters" << std::endl;
+ works = false;
+ } else {
+ works = true;
+ }
+ snd_pcm_hw_params_free (hw_params);
+
+ snd_pcm_prepare (dev);
+}
+
+RadioOutputAlsa::~RadioOutputAlsa()
+{
+ snd_pcm_close (dev);
+}
+
+bool RadioOutputAlsa::play(void *buf, int len)
+{
+ int16_t *cbuf = (int16_t *)buf;
+ int frames = len / 2;
+ int res;
+
+ if ((res = snd_pcm_writei(dev, cbuf, frames)) != frames) {
+ snd_pcm_recover(dev, res, 0);
+ snd_pcm_prepare(dev);
+ }
+ //snd_pcm_drain(dev);
+
+ return true;
+}
+
+
+RadioOutputPulse::RadioOutputPulse() : RadioOutputImplementation(),
+ pa(NULL),
+ pa_spec(NULL)
+{
+ int error;
+
+ pa_spec = (pa_sample_spec*) malloc(sizeof(pa_sample_spec));
+ pa_spec->format = PA_SAMPLE_S16LE;
+ pa_spec->rate = 24000;
+ pa_spec->channels = 2;
+
+ if (!(pa = pa_simple_new(NULL, "qtmultimedia-rtlfm-radio-plugin", PA_STREAM_PLAYBACK, NULL,
+ "radio-output", pa_spec, NULL, NULL, &error))) {
+ std::cerr << "Error connecting to PulseAudio : " << pa_strerror(error) << std::endl;
+ works = false;
+ } else {
+ std::cerr << "RadioOutputPulse::RadioOutputPulse: Connected to PulseAudio" << std::endl;
+ works = true;
+ }
+
+ extra = 0;
+ output_buf = new unsigned char[RTL_FM_MAXIMUM_BUF_LENGTH];
+
+ free(pa_spec);
+}
+
+RadioOutputPulse::~RadioOutputPulse()
+{
+ pa_simple_free(pa);
+ delete [] output_buf;
+}
+
+bool RadioOutputPulse::play(void *buf, int len)
+{
+ int error;
+ size_t n = len * 2;
+ void *p;
+
+ if (!buf) {
+ std::cerr << "Error buf == null!" << std::endl;
+ return false;
+ }
+
+ // Handle the rtl_fm code giving us an odd number of samples, which
+ // PA does not like. This extra buffer copying approach is not
+ // particularly efficient, but works for now. It looks feasible to
+ // hack in something in the demod and output thread routines in
+ // rtl_fm.c to handle it there if more performance is required.
+ p = output_buf;
+ if(extra) {
+ memcpy(output_buf, extra_buf, sizeof(int16_t));
+ if((extra + len) % 2) {
+ // We end up with len + 1 samples, n remains the same, store the extra
+ memcpy(output_buf + sizeof(int16_t), buf, n - 2);
+ memcpy(extra_buf, ((unsigned char*) buf) + n - 2, sizeof(int16_t));
+ } else {
+ // We end up with an extra sample
+ memcpy(output_buf + sizeof(int16_t), buf, n);
+ n += 2;
+ extra = 0;
+ }
+ } else if(len % 2) {
+ // We have an extra sample, store it, and decrease n
+ n -= 2;
+ memcpy(output_buf + sizeof(int16_t), buf, n);
+ memcpy(extra_buf, ((unsigned char*) buf) + n, sizeof(int16_t));
+ extra = 1;
+ } else {
+ p = buf;
+ }
+
+ if (pa_simple_write(pa, p, n, &error) < 0)
+ std::cerr << "Error writing " << n << " bytes to PulseAudio : " << pa_strerror(error) << std::endl;
+ //pa_simple_drain(pa, &error);
+
+ return true;
+}