kopia lustrzana https://github.com/windytan/slowrx
PA callbacks, sound file reading, etc
rodzic
4ac8a03567
commit
8d024adc99
|
@ -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()));
|
||||
}
|
||||
|
|
230
src/dsp.cc
230
src/dsp.cc
|
@ -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();
|
||||
}
|
||||
|
|
57
src/dsp.h
57
src/dsp.h
|
@ -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
|
||||
|
|
246
src/gui.cc
246
src/gui.cc
|
@ -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());
|
||||
|
|
85
src/gui.h
85
src/gui.h
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue