parent
6e08f8a55c
commit
0ec6475902
110
AudioOutput.cc
110
AudioOutput.cc
|
@ -5,6 +5,8 @@
|
|||
#include <fcntl.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "SoftFM.h"
|
||||
#include "AudioOutput.h"
|
||||
|
||||
|
@ -124,28 +126,88 @@ private:
|
|||
// TODO
|
||||
};
|
||||
|
||||
|
||||
/** Write audio data to ALSA device. */
|
||||
class AlsaAudioOutput
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct ALSA output stream.
|
||||
*
|
||||
* dename :: ALSA PCM device
|
||||
* samplerate :: audio sample rate in Hz
|
||||
* stereo :: true if the output stream contains stereo data
|
||||
*/
|
||||
AlsaAudioOutput(const std::string& devname,
|
||||
unsigned int samplerate,
|
||||
bool stereo);
|
||||
|
||||
~AlsaAudioOutput();
|
||||
bool write(const SampleVector& samples);
|
||||
std::string error();
|
||||
|
||||
private:
|
||||
// TODO
|
||||
};
|
||||
#endif
|
||||
|
||||
/* **************** class AlsaAudioOutput **************** */
|
||||
|
||||
// Construct ALSA output stream.
|
||||
AlsaAudioOutput::AlsaAudioOutput(const std::string& devname,
|
||||
unsigned int samplerate,
|
||||
bool stereo)
|
||||
{
|
||||
m_pcm = NULL;
|
||||
m_nchannels = stereo ? 2 : 1;
|
||||
|
||||
int r = snd_pcm_open(&m_pcm, devname.c_str(),
|
||||
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
|
||||
if (r < 0) {
|
||||
m_error = "can not open PCM device '" + devname + "' (" +
|
||||
strerror(-r) + ")";
|
||||
m_zombie = true;
|
||||
return;
|
||||
}
|
||||
|
||||
snd_pcm_nonblock(m_pcm, 0);
|
||||
|
||||
r = snd_pcm_set_params(m_pcm,
|
||||
SND_PCM_FORMAT_S16_LE,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||
m_nchannels,
|
||||
samplerate,
|
||||
1, // allow soft resampling
|
||||
500000); // latency in us
|
||||
|
||||
if (r < 0) {
|
||||
m_error = "can not set PCM parameters (";
|
||||
m_error += strerror(-r);
|
||||
m_error += ")";
|
||||
m_zombie = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Destructor.
|
||||
AlsaAudioOutput::~AlsaAudioOutput()
|
||||
{
|
||||
// Close device.
|
||||
if (m_pcm != NULL) {
|
||||
snd_pcm_close(m_pcm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write audio data.
|
||||
bool AlsaAudioOutput::write(const SampleVector& samples)
|
||||
{
|
||||
if (m_zombie)
|
||||
return false;
|
||||
|
||||
// Convert samples to bytes.
|
||||
samplesToInt16(samples, m_bytebuf);
|
||||
|
||||
// Write data.
|
||||
unsigned int p = 0;
|
||||
unsigned int n = samples.size() / m_nchannels;
|
||||
unsigned int framesize = 2 * m_nchannels;
|
||||
while (p < n) {
|
||||
|
||||
int k = snd_pcm_writei(m_pcm,
|
||||
m_bytebuf.data() + p * framesize, n - p);
|
||||
if (k < 0) {
|
||||
m_error = "write failed (";
|
||||
m_error += strerror(errno);
|
||||
m_error += ")";
|
||||
// After an underrun, ALSA keeps returning error codes until we
|
||||
// explicitly fix the stream.
|
||||
snd_pcm_recover(m_pcm, k, 0);
|
||||
return false;
|
||||
} else {
|
||||
p += k;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* end */
|
||||
|
|
|
@ -119,7 +119,9 @@ public:
|
|||
bool write(const SampleVector& samples);
|
||||
|
||||
private:
|
||||
// TODO
|
||||
unsigned int m_nchannels;
|
||||
struct _snd_pcm * m_pcm;
|
||||
std::vector<uint8_t> m_bytebuf;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
8
Makefile
8
Makefile
|
@ -2,8 +2,6 @@
|
|||
|
||||
# ----- Tweak these settings to configure for your system
|
||||
|
||||
# TODO : -D_FILE_OFFSET_BITS=64
|
||||
|
||||
CROSS =
|
||||
CFLAGS_OPT = -O2 -ffast-math -ftree-vectorize
|
||||
CFLAGS_DEBUG = -g
|
||||
|
@ -12,6 +10,7 @@ CFLAGS_PATH = -I/home/joris/test/rtl-sdr/inst/include
|
|||
CFLAGS_EXTRA =
|
||||
LDFLAGS_PATH = -L/home/joris/test/rtl-sdr/inst/lib
|
||||
LDFLAGS_EXTRA =
|
||||
LIBS_ALSA = -lasound
|
||||
LIBS_RTLSDR = /home/joris/test/rtl-sdr/inst/lib/librtlsdr.a -lusb-1.0
|
||||
LIBS_EXTRA =
|
||||
|
||||
|
@ -19,10 +18,11 @@ LIBS_EXTRA =
|
|||
|
||||
|
||||
CXX = $(CROSS)g++
|
||||
CXXFLAGS = -std=c++11 -Wall $(CFLAGS_OPT) $(CFLAGS_DEBUG) \
|
||||
CXXFLAGS = -std=c++11 -Wall -D_FILE_OFFSET_BITS=64 \
|
||||
$(CFLAGS_OPT) $(CFLAGS_DEBUG) \
|
||||
$(CFLAGS_ARCH) $(CFLAGS_PATH) $(CFLAGS_EXTRA)
|
||||
LDFLAGS = $(LDFLAGS_PATH) $(LDFLAGS_EXTRA)
|
||||
LDLIBS = $(LIBS_RTLSDR) $(LIBS_EXTRA)
|
||||
LDLIBS = $(LIBS_ALSA) $(LIBS_RTLSDR) $(LIBS_EXTRA)
|
||||
|
||||
OBJS = RtlSdrSource.o Filter.o FmDecode.o AudioOutput.o main.o
|
||||
|
||||
|
|
39
main.cc
39
main.cc
|
@ -171,6 +171,9 @@ void write_output_data(AudioOutput *output, DataBuffer<Sample> *buf,
|
|||
// Get samples from buffer and write to output.
|
||||
SampleVector samples = buf->pull();
|
||||
output->write(samples);
|
||||
if (!(*output)) {
|
||||
fprintf(stderr, "ERROR: AudioOutput: %s\n", output->error().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,12 +225,11 @@ bool parse_opt(const char *s, double& v)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
double freq = -1;
|
||||
int devidx = 0;
|
||||
double ifrate = 1.0e6;
|
||||
int pcmrate = 48000;
|
||||
int stereo = 1;
|
||||
bool stereo = true;
|
||||
enum OutputMode { MODE_RAW, MODE_WAV, MODE_ALSA };
|
||||
OutputMode outmode = MODE_ALSA;
|
||||
string filename;
|
||||
|
@ -237,7 +239,22 @@ int main(int argc, char **argv)
|
|||
fprintf(stderr,
|
||||
"SoftFM - Software decoder for FM broadcast radio with RTL-SDR\n");
|
||||
|
||||
while ((c = getopt(argc, argv, "f:d:s:r:MR:W:P::b:")) >= 0) {
|
||||
const struct option longopts[] = {
|
||||
{ "freq", 1, NULL, 'f' },
|
||||
{ "dev", 1, NULL, 'd' },
|
||||
{ "ifrate", 1, NULL, 's' },
|
||||
{ "pcmrate", 1, NULL, 'r' },
|
||||
{ "mono", 0, NULL, 'M' },
|
||||
{ "raw", 1, NULL, 'R' },
|
||||
{ "wav", 1, NULL, 'W' },
|
||||
{ "play", 2, NULL, 'P' },
|
||||
{ "buffer", 1, NULL, 'b' },
|
||||
{ NULL, 0, NULL, 0 } };
|
||||
|
||||
int c, longindex;
|
||||
while ((c = getopt_long(argc, argv,
|
||||
"f:d:s:r:MR:W:P::b:",
|
||||
longopts, &longindex)) >= 0) {
|
||||
switch (c) {
|
||||
case 'f':
|
||||
if (!parse_opt(optarg, freq) || freq <= 0) {
|
||||
|
@ -260,7 +277,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
break;
|
||||
case 'M':
|
||||
stereo = 0;
|
||||
stereo = false;
|
||||
break;
|
||||
case 'R':
|
||||
outmode = MODE_RAW;
|
||||
|
@ -377,9 +394,16 @@ int main(int argc, char **argv)
|
|||
audio_output.reset(new RawAudioOutput(filename));
|
||||
break;
|
||||
case MODE_WAV:
|
||||
case MODE_ALSA:
|
||||
// TODO
|
||||
abort();
|
||||
case MODE_ALSA:
|
||||
audio_output.reset(new AlsaAudioOutput(devname, pcmrate, stereo));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(*audio_output)) {
|
||||
fprintf(stderr, "ERROR: AudioOutput: %s\n",
|
||||
audio_output->error().c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// If buffering enabled, start background output thread.
|
||||
|
@ -424,8 +448,8 @@ int main(int argc, char **argv)
|
|||
// TODO : investigate I/Q imbalance to fix Radio4 noise
|
||||
// TODO : investigate if PLL receiver is better than phase discriminator at weak reception
|
||||
|
||||
// TODO : show mono/stereo (switching)
|
||||
|
||||
// TODO : show mono/stereo
|
||||
fprintf(stderr,
|
||||
"\rblk=%6d freq=%8.4fMHz IF=%+5.1fdB BB=%+5.1fdB audio=%+5.1fdB ",
|
||||
block,
|
||||
|
@ -461,6 +485,7 @@ int main(int argc, char **argv)
|
|||
// Join background threads.
|
||||
source_thread.join();
|
||||
if (outputbuf_samples > 0) {
|
||||
output_buffer.push_end();
|
||||
output_thread.join();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue