mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2025-05-01 16:19:47 -04:00
Now playing FM through OpenAL!
great success!
This commit is contained in:
parent
f071b2b025
commit
e0344d8efc
@ -12,22 +12,14 @@
|
||||
#define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH)
|
||||
#define AUTO_GAIN -100
|
||||
#define BUFFER_DUMP 4096
|
||||
#define MAXIMUM_RATE 2400000
|
||||
//#define MAXIMUM_RATE 2400000
|
||||
#define PI_INT (1<<14)
|
||||
#define ONE_INT (1<<14)
|
||||
static int *atan_lut = NULL;
|
||||
static int atan_lut_size = 131072; /* 512 KB */
|
||||
static int atan_lut_coef = 8;
|
||||
static uint32_t MINIMUM_RATE = 1000000;
|
||||
//static uint32_t MINIMUM_RATE = 1000000;
|
||||
|
||||
// rewrite as dynamic and thread-safe for multi demod/dongle
|
||||
#define SHARED_SIZE 6
|
||||
int16_t shared_samples[SHARED_SIZE][MAXIMUM_BUF_LENGTH];
|
||||
int ss_busy[SHARED_SIZE] = { 0 };
|
||||
|
||||
/* more cond dumbness */
|
||||
#define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m)
|
||||
#define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m)
|
||||
/* {length, coef, coef, coef} and scaled by 2^15
|
||||
for now, only length 9, optimal way to get +85% bandwidth */
|
||||
#define CIC_TABLE_MAX 10
|
||||
@ -544,18 +536,18 @@ Demodulate::Demodulate() {
|
||||
lowpassed = NULL;
|
||||
|
||||
mode_demod = &fm_demod;
|
||||
rate_in = SRATE;
|
||||
rate_in = 170000;
|
||||
rate_out = 170000;
|
||||
rate_out2 = 32000;
|
||||
output.rate = 32000;
|
||||
custom_atan = 1;
|
||||
//demod.post_downsample = 4;
|
||||
// post_downsample = 4;
|
||||
deemph = 1;
|
||||
squelch_level = 0;
|
||||
|
||||
int capture_freq;
|
||||
|
||||
downsample = (MINIMUM_RATE / rate_in) + 1;
|
||||
downsample = (SRATE / rate_in) + 1;
|
||||
if (downsample_passes) {
|
||||
downsample_passes = (int) log2(downsample) + 1;
|
||||
downsample = 1 << downsample_passes;
|
||||
@ -697,285 +689,8 @@ void Demodulate::demod(std::vector<int16_t> &buffer) {
|
||||
if (rate_out2 > 0) {
|
||||
low_pass_real(this);
|
||||
}
|
||||
|
||||
output_target->buf = lowpassed;
|
||||
output_target->len = lp_len;
|
||||
}
|
||||
|
||||
/*
|
||||
static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) {
|
||||
int i;
|
||||
struct dongle_state *s = ctx;
|
||||
struct demod_state *d = s->targets[0];
|
||||
struct demod_state *d2 = s->targets[1];
|
||||
if (do_exit) {
|
||||
return;
|
||||
}
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
if (s->mute) {
|
||||
for (i = 0; i < s->mute; i++) {
|
||||
buf[i] = 127;
|
||||
}
|
||||
s->mute = 0;
|
||||
}
|
||||
if (s->pre_rotate) {
|
||||
rotate_90(buf, len);
|
||||
}
|
||||
for (i = 0; i < (int) len; i++) {
|
||||
s->buf16[i] = (int16_t) buf[i] - 127;
|
||||
}
|
||||
if (d2 != NULL) {
|
||||
pthread_rwlock_wrlock(&d2->rw);
|
||||
d2->lowpassed = mark_shared_buffer();
|
||||
memcpy(d2->lowpassed, s->buf16, 2 * len);
|
||||
d2->lp_len = len;
|
||||
pthread_rwlock_unlock(&d2->rw);
|
||||
safe_cond_signal(&d2->ready, &d2->ready_m);
|
||||
}
|
||||
pthread_rwlock_wrlock(&d->rw);
|
||||
d->lowpassed = s->buf16;
|
||||
d->lp_len = len;
|
||||
pthread_rwlock_unlock(&d->rw);
|
||||
safe_cond_signal(&d->ready, &d->ready_m);
|
||||
s->buf16 = mark_shared_buffer();
|
||||
}
|
||||
|
||||
|
||||
static void *demod_thread_fn(void *arg) {
|
||||
struct demod_state *d = arg;
|
||||
struct buffer_bucket *o = d->output_target;
|
||||
while (!do_exit) {
|
||||
safe_cond_wait(&d->ready, &d->ready_m);
|
||||
pthread_rwlock_wrlock(&d->rw);
|
||||
full_demod(d);
|
||||
pthread_rwlock_unlock(&d->rw);
|
||||
if (d->exit_flag) {
|
||||
do_exit = 1;
|
||||
}
|
||||
pthread_rwlock_wrlock(&o->rw);
|
||||
o->buf = d->lowpassed;
|
||||
o->len = d->lp_len;
|
||||
pthread_rwlock_unlock(&o->rw);
|
||||
if (controller.freq_len > 1 && d->squelch_level && d->squelch_hits > d->conseq_squelch) {
|
||||
unmark_shared_buffer(d->lowpassed);
|
||||
d->squelch_hits = d->conseq_squelch + 1;
|
||||
safe_cond_signal(&controller.hop, &controller.hop_m);
|
||||
continue;
|
||||
}
|
||||
safe_cond_signal(&o->ready, &o->ready_m);
|
||||
pthread_mutex_lock(&o->trycond_m);
|
||||
o->trycond = 0;
|
||||
pthread_mutex_unlock(&o->trycond_m);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
static int get_nanotime(struct timespec *ts)
|
||||
{
|
||||
int rv = ENOSYS;
|
||||
#ifdef __unix__
|
||||
rv = clock_gettime(CLOCK_MONOTONIC, ts);
|
||||
#elif __APPLE__
|
||||
struct timeval tv;
|
||||
rv = gettimeofday(&tv, NULL);
|
||||
ts->tv_sec = tv.tv_sec;
|
||||
ts->tv_nsec = tv.tv_usec * 1000L;
|
||||
#endif
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
static void *output_thread_fn(void *arg) {
|
||||
int j, r = 0;
|
||||
struct output_state *s = arg;
|
||||
struct buffer_bucket *b0 = &s->results[0];
|
||||
struct buffer_bucket *b1 = &s->results[1];
|
||||
struct timespec start_time;
|
||||
struct timespec now_time;
|
||||
int64_t i, duration, samples, samples_now;
|
||||
samples = 0L;
|
||||
#ifndef _WIN32
|
||||
get_nanotime(&start_time);
|
||||
#endif
|
||||
while (!do_exit) {
|
||||
if (s->lrmix) {
|
||||
safe_cond_wait(&b0->ready, &b0->ready_m);
|
||||
pthread_rwlock_rdlock(&b0->rw);
|
||||
safe_cond_wait(&b1->ready, &b1->ready_m);
|
||||
pthread_rwlock_rdlock(&b1->rw);
|
||||
for (j = 0; j < b0->len; j++) {
|
||||
fwrite(b0->buf + j, 2, 1, s->file);
|
||||
fwrite(b1->buf + j, 2, 1, s->file);
|
||||
}
|
||||
unmark_shared_buffer(b1->buf);
|
||||
pthread_rwlock_unlock(&b1->rw);
|
||||
unmark_shared_buffer(b0->buf);
|
||||
pthread_rwlock_unlock(&b0->rw);
|
||||
continue;
|
||||
}
|
||||
if (!s->padded) {
|
||||
safe_cond_wait(&b0->ready, &b0->ready_m);
|
||||
pthread_rwlock_rdlock(&b0->rw);
|
||||
fwrite(b0->buf, 2, b0->len, s->file);
|
||||
unmark_shared_buffer(b0->buf);
|
||||
pthread_rwlock_unlock(&b0->rw);
|
||||
continue;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
// padding requires output at constant rate
|
||||
// pthread_cond_timedwait is terrible, roll our own trycond
|
||||
// figure out how to do this with windows HPET
|
||||
usleep(2000);
|
||||
pthread_mutex_lock(&b0->trycond_m);
|
||||
r = b0->trycond;
|
||||
b0->trycond = 1;
|
||||
pthread_mutex_unlock(&b0->trycond_m);
|
||||
if (r == 0) {
|
||||
pthread_rwlock_rdlock(&b0->rw);
|
||||
fwrite(b0->buf, 2, b0->len, s->file);
|
||||
unmark_shared_buffer(b0->buf);
|
||||
samples += (int64_t)b0->len;
|
||||
pthread_rwlock_unlock(&b0->rw);
|
||||
continue;
|
||||
}
|
||||
get_nanotime(&now_time);
|
||||
duration = now_time.tv_sec - start_time.tv_sec;
|
||||
duration *= 1000000000L;
|
||||
duration += (now_time.tv_nsec - start_time.tv_nsec);
|
||||
samples_now = (duration * (int64_t)s->rate) / 1000000000L;
|
||||
if (samples_now < samples) {
|
||||
continue;}
|
||||
for (i=samples; i<samples_now; i++) {
|
||||
fputc(0, s->file);
|
||||
fputc(0, s->file);
|
||||
}
|
||||
samples = samples_now;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void optimal_settings(int freq, int rate) {
|
||||
// giant ball of hacks
|
||||
// seems unable to do a single pass, 2:1
|
||||
int capture_freq, capture_rate;
|
||||
struct dongle_state *d = &dongle;
|
||||
struct demod_state *dm = &demod;
|
||||
struct controller_state *cs = &controller;
|
||||
dm->downsample = (MINIMUM_RATE / dm->rate_in) + 1;
|
||||
if (dm->downsample_passes) {
|
||||
dm->downsample_passes = (int) log2(dm->downsample) + 1;
|
||||
dm->downsample = 1 << dm->downsample_passes;
|
||||
}
|
||||
capture_freq = freq;
|
||||
capture_rate = dm->downsample * dm->rate_in;
|
||||
if (d->pre_rotate) {
|
||||
capture_freq = freq + capture_rate / 4;
|
||||
}
|
||||
capture_freq += cs->edge * dm->rate_in / 2;
|
||||
dm->output_scale = (1 << 15) / (128 * dm->downsample);
|
||||
if (dm->output_scale < 1) {
|
||||
dm->output_scale = 1;
|
||||
}
|
||||
if (dm->mode_demod == &fm_demod) {
|
||||
dm->output_scale = 1;
|
||||
}
|
||||
d->freq = (uint32_t) capture_freq;
|
||||
d->rate = (uint32_t) capture_rate;
|
||||
//d->pre_rotate = 0;
|
||||
//demod.rotate_enable = 1;
|
||||
//demod.rotate.angle = -0.25 * 2 * M_PI;
|
||||
//translate_init(&demod.rotate);
|
||||
}
|
||||
|
||||
void optimal_lrmix(void) {
|
||||
double angle1, angle2;
|
||||
uint32_t freq, freq1, freq2, bw, dongle_bw, mr;
|
||||
if (controller.freq_len != 2) {
|
||||
fprintf(stderr, "error: lrmix requires two frequencies\n");
|
||||
do_exit = 1;
|
||||
exit(1);
|
||||
}
|
||||
if (output.padded) {
|
||||
fprintf(stderr, "warning: lrmix does not support padding\n");
|
||||
}
|
||||
freq1 = controller.freqs[0];
|
||||
freq2 = controller.freqs[1];
|
||||
bw = demod.rate_out;
|
||||
freq = freq1 / 2 + freq2 / 2 + bw;
|
||||
mr = (uint32_t) abs((int64_t) freq1 - (int64_t) freq2) + bw;
|
||||
if (mr > MINIMUM_RATE) {
|
||||
MINIMUM_RATE = mr;
|
||||
}
|
||||
dongle.pre_rotate = 0;
|
||||
optimal_settings(freq, bw);
|
||||
output.padded = 0;
|
||||
clone_demod(&demod2, &demod);
|
||||
//demod2 = demod;
|
||||
demod2.output_target = &output.results[1];
|
||||
dongle.targets[1] = &demod2;
|
||||
dongle_bw = dongle.rate;
|
||||
if (dongle_bw > MAXIMUM_RATE) {
|
||||
fprintf(stderr, "error: unable to find optimal settings\n");
|
||||
do_exit = 1;
|
||||
exit(1);
|
||||
}
|
||||
angle1 = ((double) freq1 - (double) freq) / (double) dongle_bw;
|
||||
demod.rotate.angle = angle1 * 2 * M_PI;
|
||||
angle2 = ((double) freq2 - (double) freq) / (double) dongle_bw;
|
||||
demod2.rotate.angle = angle2 * 2 * M_PI;
|
||||
translate_init(&demod.rotate);
|
||||
translate_init(&demod2.rotate);
|
||||
//fprintf(stderr, "a1 %f, a2 %f\n", angle1, angle2);
|
||||
}
|
||||
static void *controller_thread_fn(void *arg) {
|
||||
// thoughts for multiple dongles
|
||||
// might be no good using a controller thread if retune/rate blocks
|
||||
int i;
|
||||
struct controller_state *s = arg;
|
||||
if (s->wb_mode) {
|
||||
for (i = 0; i < s->freq_len; i++) {
|
||||
s->freqs[i] += 16000;
|
||||
}
|
||||
}
|
||||
// set up primary channel
|
||||
optimal_settings(s->freqs[0], demod.rate_in);
|
||||
demod.squelch_level = squelch_to_rms(demod.squelch_level, &dongle, &demod);
|
||||
if (dongle.direct_sampling) {
|
||||
verbose_direct_sampling(dongle.dev, dongle.direct_sampling);
|
||||
}
|
||||
if (dongle.offset_tuning) {
|
||||
verbose_offset_tuning(dongle.dev);
|
||||
}
|
||||
// set up lrmix
|
||||
if (output.lrmix) {
|
||||
optimal_lrmix();
|
||||
}
|
||||
// Set the frequency
|
||||
verbose_set_frequency(dongle.dev, dongle.freq);
|
||||
fprintf(stderr, "Oversampling input by: %ix.\n", demod.downsample);
|
||||
fprintf(stderr, "Oversampling output by: %ix.\n", demod.post_downsample);
|
||||
fprintf(stderr, "Buffer size: %0.2fms\n", 1000 * 0.5 * (float) ACTUAL_BUF_LENGTH / (float) dongle.rate);
|
||||
// Set the sample rate
|
||||
verbose_set_sample_rate(dongle.dev, dongle.rate);
|
||||
fprintf(stderr, "Output at %u Hz.\n", demod.rate_in / demod.post_downsample);
|
||||
while (!do_exit) {
|
||||
safe_cond_wait(&s->hop, &s->hop_m);
|
||||
if (s->freq_len <= 1) {
|
||||
continue;
|
||||
}
|
||||
if (output.lrmix) {
|
||||
continue;
|
||||
}
|
||||
// hacky hopping
|
||||
s->freq_now = (s->freq_now + 1) % s->freq_len;
|
||||
optimal_settings(s->freqs[s->freq_now], demod.rate_in);
|
||||
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
||||
dongle.mute = BUFFER_DUMP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
}*/
|
||||
|
@ -16,9 +16,6 @@
|
||||
#include <algorithm>
|
||||
#include "Demodulate.h"
|
||||
|
||||
#define AL_NUM_BUFFERS 3
|
||||
#define AL_BUFFER_SIZE 4096
|
||||
|
||||
wxString glGetwxString(GLenum name) {
|
||||
const GLubyte *v = glGetString(name);
|
||||
if (v == 0) {
|
||||
@ -130,6 +127,31 @@ TestGLCanvas::TestGLCanvas(wxWindow *parent, int *attribList) :
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "Oops2\n");
|
||||
}
|
||||
|
||||
alGenBuffers(AL_NUM_BUFFERS, buffers);
|
||||
alGenSources(1, &source);
|
||||
|
||||
// prime the buffers
|
||||
ALuint buffer_init[AL_BUFFER_SIZE];
|
||||
|
||||
for (int i = 0; i < AL_BUFFER_SIZE; i++) {
|
||||
buffer_init[i] = 32767;
|
||||
}
|
||||
|
||||
format = AL_FORMAT_MONO16;
|
||||
alBufferData(buffers[0], format, buffer_init, AL_BUFFER_SIZE, 32000);
|
||||
alBufferData(buffers[1], format, buffer_init, AL_BUFFER_SIZE, 32000);
|
||||
alBufferData(buffers[2], format, buffer_init, AL_BUFFER_SIZE, 32000);
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
std::cout << "Error priming :(\n";
|
||||
}
|
||||
|
||||
alSourceQueueBuffers(source, AL_NUM_BUFFERS, buffers);
|
||||
alSourcePlay(source);
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
std::cout << "Error starting :(\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TestGLCanvas::~TestGLCanvas() {
|
||||
@ -185,8 +207,6 @@ void TestGLCanvas::setData(std::vector<signed char> *data) {
|
||||
std::vector<int16_t> tmp(data->begin(), data->end());
|
||||
demod.demod(tmp);
|
||||
|
||||
// std::cout << demod.lp_len << std::endl;
|
||||
|
||||
if (waveform_points.size() < demod.lp_len * 2) {
|
||||
waveform_points.resize(demod.lp_len * 2);
|
||||
}
|
||||
@ -205,6 +225,35 @@ void TestGLCanvas::setData(std::vector<signed char> *data) {
|
||||
waveform_points[i * 2] = ((double) i / (double) iMax);
|
||||
}
|
||||
|
||||
ALint val;
|
||||
ALuint buffer;
|
||||
|
||||
frequency = demod.output.rate;
|
||||
|
||||
alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
|
||||
if (val > 0) {
|
||||
std::cout << "buffer: " << demod.output_target->len << "@" << frequency << std::endl;
|
||||
// std::vector<ALuint> al_buffer;
|
||||
// al_buffer.resize(demod.lp_len);
|
||||
|
||||
// for (int i = 0, iMax = demod.lp_len; i < iMax; i++) {
|
||||
// al_buffer[i] = demod.lowpassed[i] + 32767.0;
|
||||
// }
|
||||
|
||||
alSourceUnqueueBuffers(source, 1, &buffer);
|
||||
alBufferData(buffer, format, demod.output_target->buf, demod.output_target->len*2, frequency);
|
||||
alSourceQueueBuffers(source, 1, &buffer);
|
||||
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
std::cout << "Error buffering :(\n";
|
||||
}
|
||||
|
||||
}
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &val);
|
||||
if (val != AL_PLAYING) {
|
||||
alSourcePlay(source);
|
||||
}
|
||||
|
||||
if (spectrum_points.size() < FFT_SIZE * 2) {
|
||||
spectrum_points.resize(FFT_SIZE * 2);
|
||||
}
|
||||
|
@ -12,12 +12,14 @@
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
|
||||
#define AL_NUM_BUFFERS 3
|
||||
#define AL_BUFFER_SIZE 4096
|
||||
|
||||
class PrimaryGLContext: public wxGLContext {
|
||||
public:
|
||||
PrimaryGLContext(wxGLCanvas *canvas);
|
||||
|
||||
void Plot(std::vector<float> &points,std::vector<float> &points2);
|
||||
void Plot(std::vector<float> &points, std::vector<float> &points2);
|
||||
|
||||
private:
|
||||
// textures for the cube faces
|
||||
@ -55,5 +57,10 @@ private:
|
||||
ALCdevice *dev;
|
||||
ALCcontext *ctx;
|
||||
|
||||
ALuint source, buffers[AL_NUM_BUFFERS];
|
||||
ALuint frequency;
|
||||
ALenum format;
|
||||
unsigned char *buf;
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user