diff options
Diffstat (limited to 'radio_output.cpp')
-rw-r--r-- | radio_output.cpp | 152 |
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; +} |