PA callbacks, sound file reading, etc

dev
Oona Räisänen 2015-09-13 22:13:45 +03:00
rodzic 4ac8a03567
commit 8d024adc99
8 zmienionych plików z 447 dodań i 225 usunięć

Wyświetl plik

@ -11,7 +11,7 @@ std::string version_string() {
}
// Clip to [0..255]
guint8 clip (double a) {
uint8_t clip (double a) {
if (a < 0) return 0;
else if (a > 255) return 255;
return round(a);
@ -32,7 +32,7 @@ double rad2deg (double rad) {
return (180 / M_PI) * rad;
}
size_t maxIndex (std::vector<double> v) {
int maxIndex (std::vector<double> v) {
const int n = sizeof(v) / sizeof(double);
return distance(v.begin(), max_element(v.begin(), v.end()));
}

Wyświetl plik

@ -2,8 +2,10 @@
#include "dsp.h"
#include "gui.h"
bool g_is_pa_initialized = false;
DSP::DSP() :
cirbuf_(CIRBUF_LEN*2), is_open_(false), t_(0), fshift_(0), sync_window_(WINDOW_HANN511),
cirbuf_(CIRBUF_LEN*2), is_open_(false), t_(0), fshift_(0), sync_window_(WINDOW_HANN511), read_buffer_(nullptr),
window_(16)
{
@ -50,38 +52,40 @@ DSP::DSP() :
void DSP::openAudioFile (std::string fname) {
if (!is_open_) {
if (is_open_)
close();
fprintf (stderr,"open '%s'\n", fname.c_str()) ;
file_ = SndfileHandle(fname.c_str()) ;
fprintf (stderr,"open '%s'\n", fname.c_str()) ;
file_ = SndfileHandle(fname.c_str()) ;
if (file_.error()) {
fprintf(stderr,"(sndfile) %s\n", file_.strError());
} else {
fprintf (stderr," opened @ %d Hz, %d ch\n", file_.samplerate(), file_.channels()) ;
if (file_.error()) {
fprintf(stderr,"sndfile: %s\n", file_.strError());
} else {
fprintf (stderr," opened @ %d Hz, %d ch\n", file_.samplerate(), file_.channels()) ;
samplerate_ = file_.samplerate();
samplerate_ = file_.samplerate();
stream_type_ = STREAM_TYPE_FILE;
stream_type_ = STREAM_TYPE_FILE;
num_chans_ = file_.channels();
read_buffer_ = new float [READ_CHUNK_LEN * num_chans_];
is_open_ = true;
readMore();
}
num_chans_ = file_.channels();
read_buffer_ = new float [READ_CHUNK_LEN * num_chans_];
is_open_ = true;
}
}
void DSP::openPortAudio () {
void DSP::openPortAudio (int n) {
if (!is_open_) {
fprintf(stderr,"open PortAudio\n");
Pa_Initialize();
if (!g_is_pa_initialized) {
printf("Pa_Initialize\n");
PaError err = Pa_Initialize();
if (err == paNoError)
g_is_pa_initialized = true;
}
PaStreamParameters inputParameters;
inputParameters.device = Pa_GetDefaultInputDevice();
inputParameters.device = (n < 0 ? Pa_GetDefaultInputDevice() : n);
inputParameters.channelCount = 1;
inputParameters.sampleFormat = paFloat32;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency ;
@ -90,6 +94,7 @@ void DSP::openPortAudio () {
const PaDeviceInfo *devinfo;
devinfo = Pa_GetDeviceInfo(inputParameters.device);
printf("Pa_OpenStream\n");
int err = Pa_OpenStream(
&pa_stream_,
&inputParameters,
@ -97,10 +102,15 @@ void DSP::openPortAudio () {
44100,
READ_CHUNK_LEN,
paClipOff,
NULL, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
&DSP::PaStaticCallback,
this );
if (!err) {
num_chans_ = 1;
printf("make readbuffer: %d floats\n",READ_CHUNK_LEN * num_chans_);
delete read_buffer_;
read_buffer_ = new float [READ_CHUNK_LEN * num_chans_]();
err = Pa_StartStream( pa_stream_ );
const PaStreamInfo *streaminfo;
@ -109,12 +119,10 @@ void DSP::openPortAudio () {
fprintf(stderr," opened '%s' @ %.1f\n",devinfo->name,samplerate_);
stream_type_ = STREAM_TYPE_PA;
num_chans_ = 1;
read_buffer_ = new float [READ_CHUNK_LEN * num_chans_]();
if (err == paNoError) {
is_open_ = true;
readMore();
//readMore();
} else {
fprintf(stderr," error at Pa_StartStream\n");
}
@ -124,6 +132,16 @@ void DSP::openPortAudio () {
}
}
void DSP::close () {
if (is_open_) {
if (stream_type_ == STREAM_TYPE_PA) {
printf("Pa_CloseStream\n");
Pa_CloseStream(pa_stream_);
}
is_open_ = false;
}
}
void DSP::set_fshift (double fshift) {
fshift_ = fshift;
}
@ -144,67 +162,93 @@ bool DSP::isLive() const {
return (stream_type_ == STREAM_TYPE_PA);
}
void DSP::readMore () {
size_t framesread = 0;
int DSP::PaCallback(const void *input, void *output,
unsigned long framesread,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags) {
if (is_open_) {
if (stream_type_ == STREAM_TYPE_FILE) {
//printf("PaCallback\n");
float* in = (float*)input;
sf_count_t fr = file_.readf(read_buffer_, READ_CHUNK_LEN);
if (fr < READ_CHUNK_LEN) {
is_open_ = false;
if (fr < 0) {
framesread = 0;
} else {
framesread = fr;
}
}
{
std::lock_guard<std::mutex> guard(buffer_mutex_);
for (unsigned i = 0; i<READ_CHUNK_LEN; i++)
read_buffer_[i] = in[i * num_chans_];
}
if (num_chans_ > 1) {
for (size_t i=0; i<READ_CHUNK_LEN; i++) {
read_buffer_[i] = read_buffer_[i*num_chans_];
}
}
readBufferTransfer(framesread);
} else if (stream_type_ == STREAM_TYPE_PA) {
return 0;
}
framesread = READ_CHUNK_LEN;
int err = Pa_ReadStream( pa_stream_, read_buffer_, READ_CHUNK_LEN );
if (err) {
fprintf(stderr,"\nPortAudio: %s\n",Pa_GetErrorText(err));
is_open_ = false;
}
void DSP::readMoreFromFile() {
unsigned long framesread = 0;
sf_count_t fr = file_.readf(read_buffer_, READ_CHUNK_LEN);
if (fr < READ_CHUNK_LEN) {
is_open_ = false;
if (fr < 0) {
framesread = 0;
} else {
framesread = fr;
}
} else {
framesread = fr;
}
if (num_chans_ > 1) {
for (size_t i=0; i<READ_CHUNK_LEN; i++) {
read_buffer_[i] = read_buffer_[i*num_chans_];
}
}
size_t cirbuf_fits = std::min(CIRBUF_LEN - cirbuf_.head, framesread);
readBufferTransfer(framesread);
}
for (size_t i=0; i<cirbuf_fits; i++)
cirbuf_.data[cirbuf_.head + i] = read_buffer_[i];
// wrap around
if (framesread > cirbuf_fits) {
for (size_t i=0; i<(framesread - cirbuf_fits); i++)
cirbuf_.data[i] = read_buffer_[cirbuf_fits + i];
void DSP::readBufferTransfer (unsigned long framesread) {
{
std::lock_guard<std::mutex> guard(buffer_mutex_);
int cirbuf_fits = std::min(CIRBUF_LEN - cirbuf_.head, int(framesread));
for (size_t i=0; i<cirbuf_fits; i++)
cirbuf_.data[cirbuf_.head + i] = read_buffer_[i];
// wrap around
if (framesread > cirbuf_fits) {
for (size_t i=0; i<(framesread - cirbuf_fits); i++)
cirbuf_.data[i] = read_buffer_[cirbuf_fits + i];
}
// mirror
for (size_t i=0; i<CIRBUF_LEN; i++)
cirbuf_.data[CIRBUF_LEN + i] = cirbuf_.data[i];
cirbuf_.head = (cirbuf_.head + framesread) % CIRBUF_LEN;
cirbuf_.fill_count += framesread;
cirbuf_.fill_count = std::min(int(cirbuf_.fill_count), CIRBUF_LEN);
}
// mirror
for (size_t i=0; i<CIRBUF_LEN; i++)
cirbuf_.data[CIRBUF_LEN + i] = cirbuf_.data[i];
cirbuf_.head = (cirbuf_.head + framesread) % CIRBUF_LEN;
cirbuf_.fill_count += framesread;
cirbuf_.fill_count = std::min(int(cirbuf_.fill_count), CIRBUF_LEN);
}
// move processing window
double DSP::forward (unsigned nsamples) {
for (unsigned i = 0; i < nsamples; i++) {
cirbuf_.tail = (cirbuf_.tail + 1) % CIRBUF_LEN;
cirbuf_.fill_count -= 1;
{
std::lock_guard<std::mutex> guard(buffer_mutex_);
cirbuf_.tail = (cirbuf_.tail + 1) % CIRBUF_LEN;
cirbuf_.fill_count -= 1;
}
if (cirbuf_.fill_count < MOMENT_LEN) {
readMore();
while (cirbuf_.fill_count < MOMENT_LEN) {
if (stream_type_ == STREAM_TYPE_PA) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
} else if (stream_type_ == STREAM_TYPE_FILE) {
readMoreFromFile();
}
}
}
}
double dt = 1.0 * nsamples / samplerate_;
@ -255,23 +299,20 @@ double DSP::peakFreq (double minf, double maxf, WindowType wintype) {
unsigned fft_len = (window_[wintype].size() <= FFT_LEN_SMALL ? FFT_LEN_SMALL : FFT_LEN_BIG);
double* mag;
mag = new double [fft_len/2 + 1]();
windowedMoment(wintype, fft_inbuf_);
fftw_execute(fft_len == FFT_LEN_BIG ? fft_plan_big_ : fft_plan_small_);
size_t peakBin = 0;
unsigned lobin = freq2bin(minf, fft_len);
unsigned hibin = freq2bin(maxf, fft_len);
for (size_t i = lobin-1; i <= hibin+1; i++) {
mag[i] = complexMag(fft_outbuf_[i]);
mag_[i] = complexMag(fft_outbuf_[i]);
if ( (i >= lobin && i <= hibin && i<(fft_len/2+1) ) &&
(peakBin == 0 || mag[i] > mag[peakBin]))
(peakBin == 0 || mag_[i] > mag_[peakBin]))
peakBin = i;
}
double result = peakBin + gaussianPeak(mag[peakBin-1], mag[peakBin], mag[peakBin+1]);
double result = peakBin + gaussianPeak(mag_[peakBin-1], mag_[peakBin], mag_[peakBin+1]);
// In Hertz
result = result / fft_len * samplerate_ + fshift_;
@ -324,12 +365,17 @@ WindowType DSP::bestWindowFor(SSTVMode Mode, double SNR) {
double DSP::videoSNR () {
if (t_ >= next_snr_time_) {
std::vector<double> bands = bandPowerPerHz({{FREQ_SYNC-1000,FREQ_SYNC-200}, {FREQ_BLACK,FREQ_WHITE}, {FREQ_WHITE+400, FREQ_WHITE+700}});
std::vector<double> bands = bandPowerPerHz(
{{FREQ_SYNC-1000,FREQ_SYNC-200},
{FREQ_BLACK,FREQ_WHITE},
{FREQ_WHITE+400, FREQ_WHITE+700}}
);
double Pvideo_plus_noise = bands[1];
double Pnoise_only = (bands[0] + bands[2]) / 2;
double Psignal = Pvideo_plus_noise - Pnoise_only;
SNR_ = ((Pnoise_only == 0 || Psignal / Pnoise_only < .01) ? -20 : 10 * log10(Psignal / Pnoise_only));
SNR_ = ((Pnoise_only == 0 || Psignal / Pnoise_only < .01) ?
-20 : 10 * log10(Psignal / Pnoise_only));
next_snr_time_ = t_ + 50e-3;
}
@ -338,7 +384,11 @@ double DSP::videoSNR () {
}
double DSP::syncPower () {
std::vector<double> bands = bandPowerPerHz({{FREQ_SYNC-50,FREQ_SYNC+50}, {FREQ_BLACK,FREQ_WHITE}}, sync_window_);
std::vector<double> bands = bandPowerPerHz(
{{FREQ_SYNC-50,FREQ_SYNC+50},
{FREQ_BLACK,FREQ_WHITE}},
sync_window_
);
double sync;
if (bands[1] == 0.0 || bands[0] > 4 * bands[1]) {
sync = 2.0;
@ -367,7 +417,6 @@ Glib::RefPtr<Gdk::Pixbuf> DSP::getLatestPixbuf() {
return latest_pixbuf_;
}
// param: y values around peak
// return: peak x position (-1 .. 1)
double gaussianPeak (double y1, double y2, double y3) {
@ -614,4 +663,25 @@ std::tuple<bool,double,double> findMelody (const Wave& wave, const Melody& melod
return { was_found, avg_fdiff, tshift };
}
std::vector<std::pair<int,std::string>> listPortaudioDevices() {
std::vector<std::pair<int,std::string>> result;
if (!g_is_pa_initialized) {
printf("Pa_Initialize\n");
PaError err = Pa_Initialize();
if (err == paNoError)
g_is_pa_initialized = true;
}
int numDevices = Pa_GetDeviceCount();
const PaDeviceInfo *device_info;
for(int i=0; i<numDevices; i++) {
device_info = Pa_GetDeviceInfo(i);
if (device_info->maxInputChannels > 0) {
result.push_back({i, device_info->name});
}
}
return result;
}
int getDefaultPaDevice() {
return Pa_GetDefaultInputDevice();
}

Wyświetl plik

@ -4,6 +4,8 @@
#include "portaudio.h"
#include <sndfile.hh>
#include <gtkmm.h>
#include <mutex>
#include <thread>
#include "fftw3.h"
#include "common.h"
@ -36,13 +38,15 @@ class DSP {
DSP();
void openAudioFile (std::string);
void openPortAudio ();
void readMore ();
void openPortAudio (int);
void close ();
void readBufferTransfer (unsigned long);
double forward (unsigned);
double forward ();
double forward_time (double);
void forward_to_time (double);
void set_fshift (double);
void readMoreFromFile();
void windowedMoment (WindowType, fftw_complex*);
double peakFreq (double, double, WindowType);
@ -51,7 +55,7 @@ class DSP {
WindowType bestWindowFor (SSTVMode, double SNR=99);
double videoSNR();
double lum(SSTVMode, bool is_adaptive=false);
bool is_open () const;
double get_t () const;
bool isLive () const;
@ -62,14 +66,24 @@ class DSP {
void setLatestPixbuf(Glib::RefPtr<Gdk::Pixbuf>);
Glib::RefPtr<Gdk::Pixbuf> getLatestPixbuf();
int PaCallback(const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags);
static int PaStaticCallback(
const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData ) {
return ((DSP*)userData)
->PaCallback(input, output, frameCount, timeInfo, statusFlags);
}
private:
//mutable Glib::Threads::Mutex Mutex;
CirBuffer cirbuf_;
//double* cirbuf_;
//int cirbuf_head_;
//int cirbuf_tail_;
//int cirbuf_fill_count_;
bool please_stop_;
float* read_buffer_;
SndfileHandle file_;
@ -78,11 +92,12 @@ class DSP {
fftw_plan fft_plan_small_;
fftw_plan fft_plan_big_;
double samplerate_;
size_t num_chans_;
int num_chans_;
PaStream *pa_stream_;
eStreamType stream_type_;
bool is_open_;
double t_;
double mag_[FFT_LEN_BIG/2+1];
double fshift_;
double next_snr_time_;
double SNR_;
@ -91,28 +106,11 @@ class DSP {
bool is_please_close_;
mutable Glib::Threads::Mutex mutex_;
std::mutex buffer_mutex_;
std::vector<Wave> window_;
};
/*class MyPortaudioClass{
int myMemberCallback(const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags);
static int myPaCallback(
const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData ) {
return ((MyPortaudioClass*)userData)
->myMemberCallback(input, output, frameCount, timeInfo, statusFlags);
}
};*/
Wave convolve (const Wave&, const Wave&, bool wrap_around=false);
Wave deriv (const Wave&);
Wave peaks (const Wave&, size_t);
@ -122,4 +120,7 @@ Wave upsample (const Wave& orig, size_t factor, int kern_type);
double gaussianPeak (double y1, double y2, double y3);
double power (fftw_complex coeff);
double complexMag (fftw_complex coeff);
std::vector<std::pair<int,std::string>> listPortaudioDevices();
int getDefaultPaDevice();
#endif

Wyświetl plik

@ -11,87 +11,119 @@ Glib::RefPtr<Gdk::Pixbuf> empty_pixbuf(int px_width) {
return pixbuf;
}
SlowGUI::SlowGUI() : redraw_dispatcher_(), resync_dispatcher_(), worker_thread_(nullptr), worker_() {
Glib::RefPtr<Gtk::Application> app =
Gtk::Application::create("com.windytan.slowrx");
SlowGUI::SlowGUI() : redraw_dispatcher_(), resync_dispatcher_(), listener_worker_thread_(nullptr), listener_worker_(),
is_aborted_by_user_(false) {
app_ = Gtk::Application::create("com.windytan.slowrx");
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create();
builder->add_from_file("ui/slowrx.ui");
builder->add_from_file("ui/aboutdialog.ui");
builder->get_widget("button_abort", button_abort);
builder->get_widget("button_browse", button_browse);
builder->get_widget("button_clear", button_clear);
builder->get_widget("button_start", button_start);
builder->get_widget("combo_card", combo_card);
builder->get_widget("combo_mode", combo_mode);
builder->get_widget("entry_picdir", entry_picdir);
builder->get_widget("eventbox_img", eventbox_img);
builder->get_widget("frame_manual", frame_manual);
builder->get_widget("frame_slant", frame_slant);
builder->get_widget("grid_vu", grid_vu);
builder->get_widget("SavedIconView", iconview);
builder->get_widget("image_devstatus", image_devstatus);
builder->get_widget("image_pwr", image_pwr);
builder->get_widget("image_rx", image_rx);
builder->get_widget("image_snr", image_snr);
builder->get_widget("label_fskid", label_fskid);
builder->get_widget("label_lastmode", label_lastmode);
builder->get_widget("label_utc", label_utc);
builder->get_widget("menuitem_quit", menuitem_quit);
builder->get_widget("menuitem_about", menuitem_about);
builder->get_widget("spin_shift", spin_shift);
builder->get_widget("statusbar", statusbar);
builder->get_widget("tog_adapt", tog_adapt);
builder->get_widget("tog_fsk", tog_fsk);
builder->get_widget("tog_rx", tog_rx);
builder->get_widget("tog_save", tog_save);
builder->get_widget("tog_setedge", tog_setedge);
builder->get_widget("tog_slant", tog_slant);
builder->get_widget("window_about", window_about);
builder->get_widget("window_main", window_main);
button_abort->signal_clicked().connect(sigc::ptr_fun(&evt_AbortRx));
button_abort->signal_clicked().connect(sigc::ptr_fun(&evt_AbortRx));
button_browse->signal_clicked().connect(sigc::ptr_fun(&evt_chooseDir));
button_clear->signal_clicked().connect(sigc::ptr_fun(&evt_clearPix));
button_start->signal_clicked().connect(sigc::ptr_fun(&evt_ManualStart));
combo_card->signal_changed().connect(sigc::ptr_fun(&evt_changeDevices));
//eventbox_img->signal_button_press_event().connect(sigc::ptr_fun(&evt_clickimg));
menuitem_quit->signal_activate().connect(sigc::ptr_fun(&evt_deletewindow));
menuitem_about->signal_activate().connect(sigc::ptr_fun(&evt_show_about));
tog_adapt->signal_activate().connect(sigc::ptr_fun(&evt_GetAdaptive));
//window_main->signal_delete_event().connect(sigc::ptr_fun(&evt_deletewindow));
//savedstore = iconview.get_model();
image_rx->set(empty_pixbuf(500));
builder->get_widget("label_lasttime", label_lasttime_);
builder->get_widget("window_about", window_about_);
builder->get_widget("window_main", window_main_);
builder->get_widget("image_rx", image_rx_);
builder->get_widget("button_abort", button_abort_);
builder->get_widget("button_clear", button_clear_);
builder->get_widget("button_manualstart", button_manualstart_);
builder->get_widget("combo_manualmode", combo_manualmode_);
builder->get_widget("combo_portaudio", combo_portaudio_);
builder->get_widget("switch_rx", switch_rx_);
builder->get_widget("switch_sync", switch_sync_);
builder->get_widget("switch_denoise", switch_denoise_);
builder->get_widget("switch_fskid", switch_fskid_);
builder->get_widget("radio_input_portaudio", radio_input_portaudio_);
builder->get_widget("radio_input_file", radio_input_file_);
builder->get_widget("radio_input_stdin", radio_input_stdin_);
builder->get_widget("button_audiofilechooser", button_audiofilechooser_);
builder->get_widget("frame_input", frame_input_);
imageReset();
//pixbuf_PWR = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, 100, 30);
//pixbuf_SNR = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, 100, 30);
combo_mode->set_active(0);
if (config.get_string("slowrx","rxdir") != NULL) {
/*if (config.get_string("slowrx","rxdir") != NULL) {
entry_picdir->set_text(config.get_string("slowrx","rxdir"));
} else {
config.set_string("slowrx","rxdir",g_get_home_dir());
entry_picdir->set_text(config.get_string("slowrx","rxdir"));
}
}*/
//setVU(0, 6);
window_main->show_all();
window_main_->show_all();
}
void SlowGUI::start() {
switch_denoise_->signal_state_flags_changed().connect(
sigc::mem_fun(this, &SlowGUI::autoChanged)
);
switch_rx_->signal_state_flags_changed().connect(
sigc::mem_fun(this, &SlowGUI::autoChanged)
);
switch_rx_->signal_state_flags_changed().connect(
sigc::mem_fun(this, &SlowGUI::autoChanged)
);
button_abort_->signal_clicked().connect(
sigc::mem_fun(this, &SlowGUI::abortedByUser)
);
button_clear_->signal_clicked().connect(
sigc::mem_fun(this, &SlowGUI::imageReset)
);
redraw_dispatcher_.connect(sigc::mem_fun(*this, &SlowGUI::onRedrawNotify));
resync_dispatcher_.connect(sigc::mem_fun(*this, &SlowGUI::onResyncNotify));
worker_thread_ = Glib::Threads::Thread::create(
sigc::bind(sigc::mem_fun(worker_, &Listener::listen), this));
std::vector<std::pair<int,std::string>> pa_devs = listPortaudioDevices();
for (std::pair<int,std::string> dev : pa_devs) {
combo_portaudio_->append(dev.second);
if (dev.first == getDefaultPaDevice()) {
combo_portaudio_->set_active(combo_portaudio_->get_children().size()-1);
}
}
radio_input_portaudio_->signal_clicked().connect(
sigc::mem_fun(this, &SlowGUI::inputDeviceChanged)
);
radio_input_file_->signal_clicked().connect(
sigc::mem_fun(this, &SlowGUI::inputDeviceChanged)
);
radio_input_stdin_->signal_clicked().connect(
sigc::mem_fun(this, &SlowGUI::inputDeviceChanged)
);
combo_portaudio_->signal_changed().connect(
sigc::mem_fun(this, &SlowGUI::inputDeviceChanged)
);
button_audiofilechooser_->signal_file_set().connect(
sigc::mem_fun(this, &SlowGUI::audioFileSelected)
);
listener_worker_thread_ = Glib::Threads::Thread::create(
sigc::bind(sigc::mem_fun(listener_worker_, &Listener::listen), this));
notReceiving();
fetchAutoState();
inputDeviceChanged();
app_->run(*window_main_);
app->run(*window_main);
}
void SlowGUI::imageReset() {
image_rx_->set(empty_pixbuf(500));
label_lasttime_->set_text("");
}
// Draw signal level meters according to given values
@ -104,7 +136,7 @@ void setVU (double *Power, int FFTLen, int WinIdx, bool ShowWin) {
rowstridePWR = pixbuf_PWR->get_rowstride();
pixelsPWR = pixbuf_PWR->get_pixels();
rowstrideSNR = pixbuf_SNR->get_rowstride();
pixelsSNR = pixbuf_SNR->get_pixels();
@ -164,13 +196,97 @@ void setVU (double *Power, int FFTLen, int WinIdx, bool ShowWin) {
}
#endif
bool SlowGUI::isRxEnabled() {
return is_rx_enabled_;
}
bool SlowGUI::isDenoiseEnabled() {
return is_denoise_enabled_;
}
bool SlowGUI::isSyncEnabled() {
return is_sync_enabled_;
}
bool SlowGUI::isAbortedByUser() {
return is_aborted_by_user_;
}
void SlowGUI::receiving() {
button_abort_->set_sensitive(true);
button_clear_->set_sensitive(false);
button_manualstart_->set_sensitive(false);
combo_manualmode_->set_sensitive(false);
frame_input_->set_sensitive(false);
}
void SlowGUI::notReceiving() {
button_abort_->set_sensitive(false);
button_clear_->set_sensitive(true);
button_manualstart_->set_sensitive(true);
combo_manualmode_->set_sensitive(true);
frame_input_->set_sensitive(true);
}
void SlowGUI::fetchAutoState() {
is_denoise_enabled_ = switch_denoise_->get_active();
is_rx_enabled_ = switch_rx_->get_active();
is_sync_enabled_ = switch_sync_->get_active();
is_fskid_enabled_ = switch_sync_->get_active();
}
void SlowGUI::abortedByUser() {
is_aborted_by_user_ = true;
}
void SlowGUI::ackAbortedByUser() {
is_aborted_by_user_ = false;
}
void SlowGUI::autoChanged(Gtk::StateFlags flags) {
fetchAutoState();
}
void SlowGUI::inputDeviceChanged() {
printf("inputDeviceChanged\n");
listener_worker_.close();
if (radio_input_portaudio_->get_active()) {
button_audiofilechooser_->set_sensitive(false);
combo_portaudio_->set_sensitive(true);
if (combo_portaudio_->get_active_row_number() >= 0) {
listener_worker_.openPortAudioDev(combo_portaudio_->get_active_row_number());
}
} else if (radio_input_file_->get_active()) {
button_audiofilechooser_->set_sensitive(true);
combo_portaudio_->set_sensitive(false);
} else if (radio_input_stdin_->get_active()) {
button_audiofilechooser_->set_sensitive(false);
combo_portaudio_->set_sensitive(false);
}
}
int SlowGUI::getSelectedPaDevice() {
return combo_portaudio_->get_active_row_number();
}
eStreamType SlowGUI::getSelectedStreamType() {
eStreamType result;
if (radio_input_portaudio_->get_active()) {
result = STREAM_TYPE_PA;
} else if (radio_input_file_->get_active()) {
result = STREAM_TYPE_FILE;
} else {
result = STREAM_TYPE_STDIN;
}
return result;
}
void SlowGUI::audioFileSelected() {
listener_worker_.openFileStream(button_audiofilechooser_->get_filename());
}
void SlowGUI::redrawNotify() {
redraw_dispatcher_.emit();
}
void SlowGUI::onRedrawNotify() {
Picture* pic = worker_.getCurrentPic();
label_lastmode->set_text(ModeSpec[pic->getMode()].name);
image_rx->set(pic->renderPixbuf(500));
Picture* pic = listener_worker_.getCurrentPic();
label_lasttime_->set_text(pic->getTimestamp() + " / " + getModeSpec(pic->getMode()).name + " ");
image_rx_->set(pic->renderPixbuf(500));
}
@ -178,14 +294,16 @@ void SlowGUI::resyncNotify() {
resync_dispatcher_.emit();
}
void SlowGUI::onResyncNotify() {
Picture* pic = worker_.getCurrentPic();
pic->resync();
if (switch_sync_->get_active()) {
Picture* pic = listener_worker_.getCurrentPic();
pic->resync();
}
}
void evt_chooseDir() {
/*Gtk::FileChooserDialog dialog (*gui.window_main, "Select folder",
Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
if (dialog.run() == Gtk::RESPONSE_ACCEPT) {
config->set_string("slowrx","rxdir",dialog.get_filename());
//gui.entry_picdir->set_text(dialog.get_filename());

Wyświetl plik

@ -11,49 +11,68 @@ class SlowGUI {
SlowGUI();
void start();
void imageReset();
void abortedByUser();
void ackAbortedByUser();
void receiving();
void notReceiving();
bool isRxEnabled();
bool isDenoiseEnabled();
bool isSyncEnabled();
bool isAbortedByUser();
void redrawNotify();
void onRedrawNotify();
void resyncNotify();
void onResyncNotify();
void fetchAutoState();
void autoChanged(Gtk::StateFlags);
void inputDeviceChanged();
void audioFileSelected();
eStreamType getSelectedStreamType();
int getSelectedPaDevice();
std::string getSelectedAudioFileName();
private:
Gtk::Button *button_abort;
Gtk::Button *button_browse;
Gtk::Button *button_clear;
Gtk::Button *button_start;
Gtk::ComboBoxText *combo_card;
Gtk::ComboBox *combo_mode;
Gtk::Entry *entry_picdir;
Gtk::EventBox *eventbox_img;
Gtk::Frame *frame_manual;
Gtk::Frame *frame_slant;
Gtk::Grid *grid_vu;
Gtk::IconView *iconview;
Gtk::Image *image_devstatus;
Gtk::Image *image_pwr;
Gtk::Image *image_rx;
Gtk::Image *image_snr;
Gtk::Label *label_fskid;
Gtk::Label *label_lastmode;
Gtk::Label *label_utc;
Gtk::MenuItem *menuitem_about;
Gtk::MenuItem *menuitem_quit;
Gtk::SpinButton *spin_shift;
Gtk::Widget *statusbar;
Gtk::ToggleButton *tog_adapt;
Gtk::ToggleButton *tog_fsk;
Gtk::ToggleButton *tog_rx;
Gtk::ToggleButton *tog_save;
Gtk::ToggleButton *tog_setedge;
Gtk::ToggleButton *tog_slant;
Gtk::Window *window_about;
Gtk::Window *window_main;
bool is_rx_enabled_;
bool is_sync_enabled_;
bool is_fskid_enabled_;
bool is_denoise_enabled_;
bool is_aborted_by_user_;
Glib::RefPtr<Gtk::Application> app_;
Gtk::Label *label_lasttime_;
Gtk::Window *window_about_;
Gtk::Window *window_main_;
Gtk::Image *image_rx_;
Gtk::ToggleButton *switch_rx_;
Gtk::ToggleButton *switch_sync_;
Gtk::ToggleButton *switch_denoise_;
Gtk::ToggleButton *switch_fskid_;
Gtk::Button *button_abort_;
Gtk::Button *button_clear_;
Gtk::Button *button_manualstart_;
Gtk::ComboBoxText *combo_manualmode_;
Gtk::ComboBoxText *combo_portaudio_;
Gtk::RadioButton *radio_input_portaudio_;
Gtk::RadioButton *radio_input_file_;
Gtk::RadioButton *radio_input_stdin_;
Gtk::FileChooserButton *button_audiofilechooser_;
Gtk::Frame *frame_input_;
Glib::Dispatcher redraw_dispatcher_;
Glib::Dispatcher resync_dispatcher_;
Glib::Threads::Thread* worker_thread_;
Listener worker_;
Glib::Threads::Thread* listener_worker_thread_;
Listener listener_worker_;
};
extern Glib::RefPtr<Gdk::Pixbuf> pixbuf_PWR;

Wyświetl plik

@ -1,7 +1,7 @@
#include "common.h"
#include "gui.h"
#include "dsp.h"
#include "picture.h"
#include "gui.h"
void Picture::pushToSyncSignal(double s) {
sync_signal_.push_back(s);
@ -17,11 +17,12 @@ double Picture::getVideoDt () const { return video_dt_; }
double Picture::getSyncDt () const { return sync_dt_; }
double Picture::getSyncSignalAt (size_t i) const { return sync_signal_[i]; }
double Picture::getVideoSignalAt (size_t i) const { return video_signal_[i]; }
std::string Picture::getTimestamp() const { return std::string(timestamp_); }
Glib::RefPtr<Gdk::Pixbuf> Picture::renderPixbuf(unsigned min_width, int upsample_factor) {
_ModeSpec m = ModeSpec[mode_];
ModeSpec m = getModeSpec(mode_);
std::vector<std::vector<std::vector<uint8_t>>> img(m.scan_pixels);
for (size_t x=0; x < m.scan_pixels; x++) {
@ -130,16 +131,16 @@ Glib::RefPtr<Gdk::Pixbuf> Picture::renderPixbuf(unsigned min_width, int upsample
unsigned img_height = round(1.0*img_width/rx_aspect + m.header_lines);
Glib::RefPtr<Gdk::Pixbuf> pixbuf_scaled;
pixbuf_scaled = pixbuf_rx->scale_simple(img_width, img_height, Gdk::INTERP_NEAREST);
pixbuf_scaled = pixbuf_rx->scale_simple(img_width, img_height, Gdk::INTERP_HYPER);
pixbuf_rx->save("testi.png", "png");
//pixbuf_rx->save("testi.png", "png");
return pixbuf_scaled;
}
void Picture::saveSync () {
_ModeSpec m = ModeSpec[mode_];
ModeSpec m = getModeSpec(mode_);
int line_width = m.t_period / sync_dt_;
int numlines = 240;
int upsample_factor = 2;
@ -178,7 +179,7 @@ void Picture::resync () {
return;
#endif
_ModeSpec m = ModeSpec[mode_];
ModeSpec m = getModeSpec(mode_);
int line_width = m.t_period / sync_dt_;
size_t upsample_factor = 2;
@ -247,7 +248,7 @@ void Picture::resync () {
// Time instants for all pixels
std::vector<PixelSample> pixelSamplingPoints(SSTVMode mode) {
_ModeSpec m = ModeSpec[mode];
ModeSpec m = getModeSpec(mode);
std::vector<PixelSample> pixel_grid;
for (size_t y=0; y<m.num_lines; y++) {
for (size_t x=0; x<m.scan_pixels; x++) {
@ -272,13 +273,13 @@ std::vector<PixelSample> pixelSamplingPoints(SSTVMode mode) {
else if (m.family == MODE_PD) {
double line_video_start = (y/2)*(m.t_period) + m.t_sync + m.t_porch;
if (ch == 0) {
px.t = line_video_start + (y%2 == 1 ? 3*m.t_scan : 0) +
px.t = line_video_start + (y%2 == 1 ? 3*m.t_scan : 0) +
(x+.5)/m.scan_pixels * m.t_scan;
} else if (ch == 1 && (y%2) == 0) {
px.t = line_video_start + m.t_scan +
px.t = line_video_start + m.t_scan +
(x+.5)/m.scan_pixels * m.t_scan;
} else if (ch == 2 && (y%2) == 0) {
px.t = line_video_start + 2*m.t_scan +
px.t = line_video_start + 2*m.t_scan +
(x+.5)/m.scan_pixels * m.t_scan;
} else {
exists = false;
@ -326,3 +327,7 @@ std::vector<PixelSample> pixelSamplingPoints(SSTVMode mode) {
return pixel_grid;
}
void Picture::save() {
std::string filename = std::string("slowrx_") + safe_timestamp_ + ".png";
std::cout << filename << "\n";
}

Wyświetl plik

@ -2,7 +2,8 @@
#define PICTURE_H_
#include "common.h"
#include "gui.h"
#include <gtkmm.h>
#include <ctime>
class Picture {
@ -10,9 +11,13 @@ class Picture {
Picture(SSTVMode _mode)
: mode_(_mode), pixel_grid_(pixelSamplingPoints(_mode)), video_signal_(),
video_dt_(ModeSpec[_mode].t_scan/ModeSpec[_mode].scan_pixels/2), sync_signal_(),
sync_dt_(ModeSpec[_mode].t_period / ModeSpec[_mode].scan_pixels/3), drift_(1.0),
starts_at_(0.0) {}
video_dt_(getModeSpec(_mode).t_scan/getModeSpec(_mode).scan_pixels/2), sync_signal_(),
sync_dt_(getModeSpec(_mode).t_period / getModeSpec(_mode).scan_pixels/3), drift_(1.0),
starts_at_(0.0) {
std::time_t t = std::time(NULL);
std::strftime(timestamp_, sizeof(timestamp_),"%F %Rz", std::gmtime(&t));
std::strftime(safe_timestamp_, sizeof(timestamp_),"%Y%m%d_%H%M%SZ", std::gmtime(&t));
}
void pushToSyncSignal (double s);
void pushToVideoSignal (double s);
@ -24,11 +29,12 @@ class Picture {
double getSyncDt () const;
double getSyncSignalAt(size_t i) const;
double getVideoSignalAt(size_t i) const;
std::string getTimestamp() const;
Glib::RefPtr<Gdk::Pixbuf> renderPixbuf(unsigned min_width=320, int upsample_factor=4);
void resync();
void saveSync();
void save();
private:
SSTVMode mode_;
@ -39,6 +45,8 @@ class Picture {
double sync_dt_;
double drift_;
double starts_at_;
char safe_timestamp_[100];
char timestamp_[100];
};
#endif

Wyświetl plik

@ -4,10 +4,10 @@
#include "dsp.h"
int main(int argc, char *argv[]) {
/*
std::string confpath(std::string(getenv("HOME")) + "/.config/slowrx/slowrx.ini");
config.load_from_file(confpath);
*/
int opt_char;
while ((opt_char = getopt (argc, argv, "t:f:")) != EOF)
switch (opt_char) {
@ -19,9 +19,10 @@ int main(int argc, char *argv[]) {
break;
}
//Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "com.windytan.slowrx");
listPortaudioDevices();
SlowGUI gui;
gui.start();
return 0;
}