2024-12-26 11:48:03 +08:00
|
|
|
#include <iostream>
|
2024-12-26 16:18:43 +08:00
|
|
|
#include <thread>
|
|
|
|
#include <cstdint>
|
2024-12-27 18:56:31 +08:00
|
|
|
#include <complex>
|
2024-12-31 23:39:07 +08:00
|
|
|
#include <optional>
|
2025-01-18 15:46:45 +08:00
|
|
|
#include <chrono>
|
2024-12-26 11:48:03 +08:00
|
|
|
|
2024-12-26 12:33:11 +08:00
|
|
|
#include <GLFW/glfw3.h>
|
2024-12-26 16:18:43 +08:00
|
|
|
#include <sndio.h>
|
2024-12-26 17:33:59 +08:00
|
|
|
#include <poll.h>
|
2024-12-26 16:18:43 +08:00
|
|
|
#include <math.h>
|
2024-12-26 12:33:11 +08:00
|
|
|
|
|
|
|
#include "imgui.h"
|
|
|
|
#include "imgui_impl_glfw.h"
|
|
|
|
#include "imgui_impl_opengl3.h"
|
|
|
|
|
2024-12-30 23:45:14 +08:00
|
|
|
#include "dsp_lib.h"
|
2024-12-31 23:39:07 +08:00
|
|
|
#include "fifo.h"
|
2024-12-30 23:45:14 +08:00
|
|
|
|
2024-12-28 19:26:40 +08:00
|
|
|
#define SND_BITS 24
|
2024-12-26 16:18:43 +08:00
|
|
|
#define SND_PCHAN 2
|
2024-12-28 19:04:35 +08:00
|
|
|
#define SND_RCHAN 2
|
2025-01-09 12:13:28 +08:00
|
|
|
#define SND_RATE 192000
|
2024-12-26 16:18:43 +08:00
|
|
|
#define SND_BUFLEN 4096
|
|
|
|
|
2024-12-27 16:31:44 +08:00
|
|
|
static std::atomic<bool> shutdown_threads;
|
2025-01-02 15:31:56 +08:00
|
|
|
|
2024-12-27 19:15:37 +08:00
|
|
|
static std::atomic<double> frequency[SND_PCHAN];
|
2025-01-02 12:26:00 +08:00
|
|
|
static std::atomic<double> amplitude[SND_PCHAN];
|
2024-12-27 18:56:31 +08:00
|
|
|
|
2025-01-02 13:52:18 +08:00
|
|
|
#define HIST_DEPTH 512
|
|
|
|
|
2025-01-02 15:31:56 +08:00
|
|
|
static std::atomic<double> peak;
|
|
|
|
static std::atomic<bool> clipped;
|
|
|
|
static std::atomic<int> in_wave_trigger;
|
|
|
|
static std::mutex in_wave_mutex;
|
|
|
|
static float in_wave[HIST_DEPTH];
|
|
|
|
|
2025-01-05 15:32:26 +08:00
|
|
|
static std::atomic<int> multiplier[SND_PCHAN];
|
2025-01-02 13:52:18 +08:00
|
|
|
static std::atomic<double> lpf_bandwidth[SND_PCHAN];
|
2025-01-18 15:55:34 +08:00
|
|
|
static std::atomic<double> li_mag[SND_PCHAN];
|
|
|
|
static std::atomic<double> li_phase[SND_PCHAN];
|
2025-01-02 12:26:00 +08:00
|
|
|
static std::mutex li_hist_mutex;
|
2025-01-18 15:55:34 +08:00
|
|
|
static std::atomic<bool> li_hist_hold[SND_PCHAN];
|
2025-01-02 13:52:18 +08:00
|
|
|
static float li_hist_mag[SND_PCHAN][HIST_DEPTH];
|
|
|
|
static float li_hist_phase[SND_PCHAN][HIST_DEPTH];
|
2024-12-27 16:31:44 +08:00
|
|
|
|
2025-01-02 15:31:56 +08:00
|
|
|
enum class InWaveState { delay, wait_trigger, capturing };
|
|
|
|
|
2024-12-26 16:18:43 +08:00
|
|
|
static void dsp_thread()
|
|
|
|
{
|
|
|
|
struct sio_hdl *hdl;
|
|
|
|
struct sio_par par;
|
|
|
|
|
2024-12-26 17:45:59 +08:00
|
|
|
hdl = sio_open(SIO_DEVANY, SIO_PLAY|SIO_REC, 1);
|
2024-12-26 16:18:43 +08:00
|
|
|
if(hdl == nullptr) {
|
|
|
|
std::cerr << "failed to open sound device" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sio_initpar(&par);
|
2024-12-27 16:31:44 +08:00
|
|
|
par.sig = 1;
|
2024-12-26 16:18:43 +08:00
|
|
|
par.bits = SND_BITS;
|
|
|
|
par.pchan = SND_PCHAN;
|
2024-12-28 19:04:35 +08:00
|
|
|
par.rchan = SND_RCHAN;
|
2024-12-26 16:18:43 +08:00
|
|
|
par.rate = SND_RATE;
|
|
|
|
par.le = SIO_LE_NATIVE;
|
2024-12-26 17:33:59 +08:00
|
|
|
par.xrun = SIO_ERROR;
|
2024-12-26 16:18:43 +08:00
|
|
|
if(!sio_setpar(hdl, &par)) {
|
|
|
|
std::cerr << "failed to set sound device parameters" << std::endl;
|
|
|
|
sio_close(hdl);
|
|
|
|
return;
|
|
|
|
}
|
2024-12-26 17:33:59 +08:00
|
|
|
if(!sio_getpar(hdl, &par)) {
|
|
|
|
std::cerr << "failed to get back sound device parameters" << std::endl;
|
|
|
|
sio_close(hdl);
|
|
|
|
return;
|
|
|
|
}
|
2024-12-26 16:18:43 +08:00
|
|
|
|
|
|
|
if(!sio_start(hdl)) {
|
|
|
|
std::cerr << "failed to start sound device" << std::endl;
|
|
|
|
sio_close(hdl);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-12-30 23:45:14 +08:00
|
|
|
DDS dds[SND_PCHAN];
|
2024-12-28 19:26:40 +08:00
|
|
|
int32_t buf_out[SND_BUFLEN*SND_PCHAN];
|
2024-12-27 19:21:58 +08:00
|
|
|
size_t buf_out_offset = sizeof(buf_out);
|
2024-12-27 18:56:31 +08:00
|
|
|
|
2024-12-31 23:39:07 +08:00
|
|
|
int32_t buf_in[SND_BUFLEN*SND_RCHAN];
|
|
|
|
size_t buf_in_offset = sizeof(buf_in);
|
|
|
|
|
2025-01-18 15:38:39 +08:00
|
|
|
FIFO<phase_t, 32> ftw_fifo[SND_PCHAN];
|
2024-12-31 23:39:07 +08:00
|
|
|
|
2025-01-02 15:31:56 +08:00
|
|
|
int clipped_count = 0;
|
|
|
|
double peak_running = 0.0;
|
|
|
|
int peak_count = 0;
|
|
|
|
phase_t last_trig_phase = 0;
|
|
|
|
InWaveState in_wave_state = InWaveState::delay;
|
|
|
|
int in_wave_count = 0;
|
|
|
|
float in_wave_buf[HIST_DEPTH];
|
|
|
|
|
2024-12-31 16:57:42 +08:00
|
|
|
Lockin<4> lockin[SND_PCHAN];
|
|
|
|
for(int i=0;i<SND_PCHAN;i++)
|
|
|
|
// input channels are averaged together to reduce uncorrelated noise
|
|
|
|
lockin[i].set_scale(pow(0.5, SND_BITS-1)/SND_RCHAN);
|
2025-01-02 12:26:00 +08:00
|
|
|
int li_count[SND_PCHAN] = { 0 };
|
2024-12-27 18:56:31 +08:00
|
|
|
|
2024-12-26 17:33:59 +08:00
|
|
|
nfds_t nfds = sio_nfds(hdl);
|
|
|
|
struct pollfd pfd[nfds];
|
2024-12-26 16:18:43 +08:00
|
|
|
while(!shutdown_threads) {
|
2024-12-26 17:45:59 +08:00
|
|
|
sio_pollfd(hdl, pfd, POLLOUT|POLLIN);
|
2024-12-26 17:33:59 +08:00
|
|
|
poll(pfd, nfds, INFTIM);
|
|
|
|
int revents = sio_revents(hdl, pfd);
|
|
|
|
|
2025-01-05 15:32:26 +08:00
|
|
|
for(int i=0;i<SND_PCHAN;i++) {
|
|
|
|
lockin[i].set_multiplier(multiplier[i]);
|
2024-12-31 16:57:42 +08:00
|
|
|
lockin[i].set_bandwidth(lpf_bandwidth[i]/SND_RATE);
|
2025-01-05 15:32:26 +08:00
|
|
|
}
|
2024-12-27 16:31:44 +08:00
|
|
|
|
2024-12-26 17:33:59 +08:00
|
|
|
if(revents & POLLOUT) {
|
2024-12-27 19:21:58 +08:00
|
|
|
if(buf_out_offset == sizeof(buf_out)) {
|
2024-12-31 23:39:07 +08:00
|
|
|
for(int i=0;i<SND_PCHAN;i++) {
|
|
|
|
phase_t ftw = frequency_to_ftw(frequency[i]/SND_RATE);
|
|
|
|
dds[i].ftw = ftw;
|
|
|
|
if(!ftw_fifo[i].push(ftw))
|
|
|
|
std::cerr << "FTW FIFO overflow" << std::endl;
|
|
|
|
}
|
2025-01-02 12:26:00 +08:00
|
|
|
for(int i=0;i<SND_PCHAN;i++) {
|
|
|
|
double scale = amplitude[i]*(pow(2.0, SND_BITS-1) - 1.0);
|
2024-12-30 23:45:14 +08:00
|
|
|
for(int j=0;j<SND_BUFLEN;j++)
|
|
|
|
buf_out[SND_PCHAN*j+i] = scale*dds[i].get();
|
2025-01-02 12:26:00 +08:00
|
|
|
}
|
2024-12-27 19:21:58 +08:00
|
|
|
buf_out_offset = 0;
|
2024-12-26 17:33:59 +08:00
|
|
|
}
|
2024-12-27 19:21:58 +08:00
|
|
|
size_t written = sio_write(hdl, (const char *)buf_out + buf_out_offset, sizeof(buf_out) - buf_out_offset);
|
|
|
|
buf_out_offset += written;
|
2024-12-26 17:45:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if(revents & POLLIN) {
|
2024-12-27 18:56:31 +08:00
|
|
|
size_t read = sio_read(hdl, buf_in, sizeof(buf_in));
|
2024-12-31 23:39:07 +08:00
|
|
|
buf_in_offset += read;
|
|
|
|
if(buf_in_offset >= sizeof(buf_in)) {
|
|
|
|
for(int i=0;i<SND_PCHAN;i++) {
|
|
|
|
std::optional<phase_t> ftw = ftw_fifo[i].pull();
|
|
|
|
if(ftw.has_value())
|
|
|
|
lockin[i].ftw = ftw.value();
|
|
|
|
else
|
|
|
|
std::cerr << "FTW FIFO underflow" << std::endl;
|
|
|
|
}
|
|
|
|
buf_in_offset -= sizeof(buf_in);
|
|
|
|
}
|
2025-01-02 15:31:56 +08:00
|
|
|
for(int i=0;i<read/(SND_RCHAN*sizeof(buf_in[0]));i++) {
|
|
|
|
double sample = 0.0;
|
|
|
|
for(int j=0;j<SND_RCHAN;j++) {
|
|
|
|
double sample_d = (double)buf_in[SND_RCHAN*i+j];
|
|
|
|
sample += sample_d;
|
|
|
|
if(std::abs(sample_d) > 0.99*pow(2.0, SND_BITS-1))
|
|
|
|
// display the clipped indicator for about one second
|
|
|
|
clipped_count = SND_RATE;
|
|
|
|
}
|
|
|
|
if(clipped_count > 0) {
|
|
|
|
clipped_count--;
|
|
|
|
clipped = true;
|
|
|
|
} else
|
|
|
|
clipped = false;
|
|
|
|
|
|
|
|
double sample_abs = std::abs(sample);
|
|
|
|
if(sample_abs > peak_running)
|
|
|
|
peak_running = sample_abs;
|
|
|
|
peak_count++;
|
|
|
|
if(peak_count == SND_RATE/10) {
|
|
|
|
peak_count = 0;
|
|
|
|
peak = peak_running/(pow(2.0, SND_BITS-1)*SND_RCHAN);
|
|
|
|
peak_running = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
phase_t trig_phase = lockin[in_wave_trigger].get_phase();
|
|
|
|
bool trigger = trig_phase > last_trig_phase;
|
|
|
|
last_trig_phase = trig_phase;
|
|
|
|
switch(in_wave_state) {
|
|
|
|
case InWaveState::delay:
|
|
|
|
in_wave_count++;
|
|
|
|
if(in_wave_count == SND_RATE/10)
|
|
|
|
in_wave_state = InWaveState::wait_trigger;
|
|
|
|
break;
|
|
|
|
case InWaveState::wait_trigger:
|
|
|
|
if(trigger) {
|
|
|
|
in_wave_count = 0;
|
|
|
|
in_wave_state = InWaveState::capturing;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case InWaveState::capturing:
|
|
|
|
in_wave_buf[in_wave_count++] = sample/(pow(2.0, SND_BITS-1)*SND_RCHAN);
|
|
|
|
if(in_wave_count == HIST_DEPTH) {
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> guard(in_wave_mutex);
|
|
|
|
std::memcpy(in_wave, in_wave_buf, sizeof(in_wave));
|
|
|
|
}
|
|
|
|
in_wave_count = 0;
|
|
|
|
in_wave_state = InWaveState::delay;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int j=0;j<SND_PCHAN;j++) {
|
|
|
|
std::complex<double> lockin_out = lockin[j].update(sample);
|
2025-01-18 15:55:34 +08:00
|
|
|
double mag = std::abs(lockin_out);
|
|
|
|
double phase = std::arg(lockin_out);
|
2025-01-02 15:31:56 +08:00
|
|
|
|
2025-01-18 15:55:34 +08:00
|
|
|
li_mag[j] = mag;
|
|
|
|
li_phase[j] = phase;
|
|
|
|
if(!li_hist_hold[j]) {
|
2025-01-02 15:31:56 +08:00
|
|
|
li_count[j]++;
|
|
|
|
if(li_count[j] == 200) {
|
|
|
|
li_count[j] = 0;
|
2025-01-02 12:26:00 +08:00
|
|
|
std::lock_guard<std::mutex> guard(li_hist_mutex);
|
2025-01-02 15:31:56 +08:00
|
|
|
std::memmove(&li_hist_mag[j][0], &li_hist_mag[j][1], (HIST_DEPTH-1)*sizeof(float));
|
2025-01-18 15:55:34 +08:00
|
|
|
li_hist_mag[j][HIST_DEPTH-1] = mag;
|
2025-01-02 15:31:56 +08:00
|
|
|
std::memmove(&li_hist_phase[j][0], &li_hist_phase[j][1], (HIST_DEPTH-1)*sizeof(float));
|
2025-01-18 15:55:34 +08:00
|
|
|
li_hist_phase[j][HIST_DEPTH-1] = phase;
|
2025-01-02 12:26:00 +08:00
|
|
|
}
|
2024-12-27 18:56:31 +08:00
|
|
|
}
|
|
|
|
}
|
2025-01-02 15:31:56 +08:00
|
|
|
}
|
2024-12-26 16:18:43 +08:00
|
|
|
}
|
2024-12-27 15:14:08 +08:00
|
|
|
|
|
|
|
if(sio_eof(hdl)) {
|
|
|
|
std::cerr << "sound I/O error" << std::endl;
|
|
|
|
sio_close(hdl);
|
|
|
|
return;
|
|
|
|
}
|
2024-12-26 16:18:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
sio_stop(hdl);
|
|
|
|
sio_close(hdl);
|
|
|
|
}
|
|
|
|
|
2025-01-18 15:46:45 +08:00
|
|
|
static void servo_thread(int channel)
|
|
|
|
{
|
|
|
|
Clocker clocker = Clocker(std::chrono::milliseconds(100));
|
|
|
|
while(!shutdown_threads) {
|
|
|
|
clocker.tick();
|
|
|
|
std::cout << "servo thread tick " << channel << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-26 12:33:11 +08:00
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
|
|
|
if(!glfwInit()) {
|
|
|
|
std::cerr << "failed to initialize GLFW" << std::endl;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
std::atexit(glfwTerminate);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
2025-01-18 15:38:58 +08:00
|
|
|
static GLFWwindow *window = glfwCreateWindow(1024, 1200, "Soundlocker", nullptr, nullptr);
|
2024-12-26 12:33:11 +08:00
|
|
|
if(window == nullptr) {
|
|
|
|
std::cerr << "failed to create GLFW window" << std::endl;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
static auto DestroyWindow = []() {
|
|
|
|
glfwDestroyWindow(window);
|
|
|
|
};
|
|
|
|
std::atexit(DestroyWindow);
|
|
|
|
glfwMakeContextCurrent(window);
|
|
|
|
glfwSwapInterval(1);
|
|
|
|
|
|
|
|
IMGUI_CHECKVERSION();
|
|
|
|
ImGui::CreateContext();
|
|
|
|
static auto ImGuiDestroyContext = []() {
|
|
|
|
ImGui::DestroyContext();
|
|
|
|
};
|
|
|
|
std::atexit(ImGuiDestroyContext);
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
|
|
|
std::atexit(ImGui_ImplGlfw_Shutdown);
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 130");
|
|
|
|
std::atexit(ImGui_ImplOpenGL3_Shutdown);
|
|
|
|
|
2024-12-27 18:56:31 +08:00
|
|
|
for(int i=0;i<SND_PCHAN;i++) {
|
2024-12-27 19:15:37 +08:00
|
|
|
frequency[i] = 441.0 + 202.0*i;
|
2025-01-02 12:26:00 +08:00
|
|
|
amplitude[i] = 1.0;
|
2025-01-05 15:32:26 +08:00
|
|
|
multiplier[i] = 1;
|
2024-12-27 19:15:37 +08:00
|
|
|
lpf_bandwidth[i] = 10.0;
|
2024-12-27 18:56:31 +08:00
|
|
|
}
|
2024-12-27 16:31:44 +08:00
|
|
|
|
2024-12-26 16:18:43 +08:00
|
|
|
static std::thread dsp_thread_h = std::thread(dsp_thread);
|
|
|
|
static auto JoinDSP = []() {
|
|
|
|
dsp_thread_h.join();
|
|
|
|
};
|
|
|
|
std::atexit(JoinDSP);
|
|
|
|
|
2025-01-18 15:46:45 +08:00
|
|
|
static std::thread servo_thread_h[SND_PCHAN];
|
|
|
|
for(int i=0;i<SND_PCHAN;i++)
|
|
|
|
servo_thread_h[i] = std::thread(servo_thread, i);
|
|
|
|
static auto JoinServo = []() {
|
|
|
|
for(int i=0;i<SND_PCHAN;i++)
|
|
|
|
servo_thread_h[i].join();
|
|
|
|
};
|
|
|
|
std::atexit(JoinServo);
|
|
|
|
|
2024-12-26 16:18:43 +08:00
|
|
|
shutdown_threads = false;
|
|
|
|
static auto SetShutdown = []() {
|
|
|
|
shutdown_threads = true;
|
|
|
|
};
|
|
|
|
std::atexit(SetShutdown);
|
|
|
|
|
2025-01-01 10:49:28 +08:00
|
|
|
float plot_scale[SND_PCHAN];
|
|
|
|
for(int i=0;i<SND_PCHAN;i++)
|
2025-01-02 12:26:00 +08:00
|
|
|
plot_scale[i] = 0.05f;
|
|
|
|
while(!glfwWindowShouldClose(window)) {
|
2024-12-26 12:33:11 +08:00
|
|
|
glfwPollEvents();
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
|
|
ImGui_ImplGlfw_NewFrame();
|
|
|
|
ImGui::NewFrame();
|
|
|
|
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f));
|
|
|
|
ImGui::SetNextWindowSize(io.DisplaySize);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
2025-01-18 15:38:58 +08:00
|
|
|
ImGui::Begin("Soundlocker", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize);
|
2024-12-26 12:33:11 +08:00
|
|
|
|
2025-01-02 12:26:00 +08:00
|
|
|
if(ImGui::CollapsingHeader("Modulation")) {
|
2024-12-27 16:31:44 +08:00
|
|
|
for(int i=0;i<SND_PCHAN;i++) {
|
|
|
|
char str[64];
|
2025-01-02 12:26:00 +08:00
|
|
|
sprintf(str, "Channel %d", i);
|
|
|
|
ImGui::SeparatorText(str);
|
2025-01-01 10:49:28 +08:00
|
|
|
sprintf(str, "frequency##%d", i);
|
2024-12-27 16:31:44 +08:00
|
|
|
float frequency_l = frequency[i];
|
|
|
|
ImGui::SliderFloat(str, &frequency_l, 50.0f, 8000.0f);
|
|
|
|
frequency[i] = frequency_l;
|
2025-01-02 12:26:00 +08:00
|
|
|
sprintf(str, "amplitude##%d", i);
|
|
|
|
float amplitude_l = amplitude[i];
|
|
|
|
ImGui::SliderFloat(str, &litude_l, 0.0f, 1.0f);
|
|
|
|
amplitude[i] = amplitude_l;
|
2024-12-27 16:31:44 +08:00
|
|
|
}
|
|
|
|
}
|
2025-01-02 15:31:56 +08:00
|
|
|
if(ImGui::CollapsingHeader("Input monitor")) {
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
|
|
ImGui::Text("level:");
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::ProgressBar((float)peak, ImVec2(30.0f*ImGui::GetFontSize(), 0.0f));
|
|
|
|
if(clipped) {
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "clipped!");
|
|
|
|
}
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
|
|
ImGui::Text("trigger:");
|
|
|
|
ImGui::SameLine();
|
|
|
|
int in_wave_trigger_l = in_wave_trigger;
|
|
|
|
char str[64];
|
|
|
|
for(int i=0;i<SND_PCHAN;i++) {
|
|
|
|
sprintf(str, "ch%d", i);
|
|
|
|
ImGui::RadioButton(str, &in_wave_trigger_l, i);
|
|
|
|
if(i < (SND_PCHAN-1)) ImGui::SameLine();
|
|
|
|
}
|
|
|
|
in_wave_trigger = in_wave_trigger_l;
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> guard(in_wave_mutex);
|
|
|
|
ImGui::PlotLines("waveform", in_wave, HIST_DEPTH, 0, 0, -1.0f, 1.0f, ImVec2(0.0f, 200.0f));
|
|
|
|
}
|
|
|
|
}
|
2024-12-27 18:56:31 +08:00
|
|
|
if(ImGui::CollapsingHeader("Demodulation", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
for(int i=0;i<SND_PCHAN;i++) {
|
|
|
|
char str[64];
|
2025-01-01 10:49:28 +08:00
|
|
|
|
2025-01-02 12:26:00 +08:00
|
|
|
sprintf(str, "Channel %d", i);
|
|
|
|
ImGui::SeparatorText(str);
|
2025-01-05 15:32:26 +08:00
|
|
|
ImGui::AlignTextToFramePadding();
|
|
|
|
ImGui::Text("multiplier:");
|
|
|
|
int multiplier_l = multiplier[i];
|
|
|
|
ImGui::SameLine();
|
|
|
|
sprintf(str, "f##%d", i);
|
|
|
|
ImGui::RadioButton(str, &multiplier_l, 1);
|
|
|
|
ImGui::SameLine();
|
|
|
|
sprintf(str, "2f##%d", i);
|
|
|
|
ImGui::RadioButton(str, &multiplier_l, 2);
|
|
|
|
ImGui::SameLine();
|
|
|
|
sprintf(str, "3f##%d", i);
|
|
|
|
ImGui::RadioButton(str, &multiplier_l, 3);
|
|
|
|
multiplier[i] = multiplier_l;
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text("/");
|
2025-01-18 15:55:34 +08:00
|
|
|
bool hold_l = li_hist_hold[i];
|
2025-01-05 15:32:26 +08:00
|
|
|
ImGui::SameLine();
|
2025-01-18 15:55:34 +08:00
|
|
|
sprintf(str, "hold plot##%d", i);
|
|
|
|
ImGui::Checkbox(str, &hold_l);
|
|
|
|
li_hist_hold[i] = hold_l;
|
2025-01-01 10:49:28 +08:00
|
|
|
sprintf(str, "LPF BW##%d", i);
|
2024-12-27 18:56:31 +08:00
|
|
|
float lpf_bandwidth_l = lpf_bandwidth[i];
|
|
|
|
ImGui::SliderFloat(str, &lpf_bandwidth_l, 0.5f, 200.0f);
|
|
|
|
lpf_bandwidth[i] = lpf_bandwidth_l;
|
|
|
|
|
2025-01-02 12:26:00 +08:00
|
|
|
sprintf(str, "mag scale##%d", i);
|
|
|
|
ImGui::SliderFloat(str, &plot_scale[i], 0.01f, 0.1f);
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> guard(li_hist_mutex);
|
|
|
|
sprintf(str, "magnitude##%d", i);
|
2025-01-02 13:52:18 +08:00
|
|
|
ImGui::PlotLines(str, li_hist_mag[i], HIST_DEPTH, 0, 0, -0.0f, 0.1f*plot_scale[i], ImVec2(0.0f, 200.0f));
|
2025-01-02 12:26:00 +08:00
|
|
|
sprintf(str, "phase##%d", i);
|
2025-01-02 13:52:18 +08:00
|
|
|
ImGui::PlotLines(str, li_hist_phase[i], HIST_DEPTH, 0, 0, -M_PI, M_PI, ImVec2(0.0f, 200.0f));
|
2024-12-27 18:56:31 +08:00
|
|
|
}
|
2025-01-18 15:55:34 +08:00
|
|
|
ImGui::Text("values: %8.5f %8.5f rad", (double)li_mag[i], (double)li_phase[i]);
|
2024-12-27 18:56:31 +08:00
|
|
|
}
|
|
|
|
}
|
2024-12-26 12:33:11 +08:00
|
|
|
|
|
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleVar(1);
|
|
|
|
|
|
|
|
ImGui::Render();
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
int display_w, display_h;
|
|
|
|
glfwGetFramebufferSize(window, &display_w, &display_h);
|
|
|
|
glViewport(0, 0, display_w, display_h);
|
|
|
|
glfwSwapBuffers(window);
|
|
|
|
}
|
|
|
|
|
2024-12-26 11:48:03 +08:00
|
|
|
return 0;
|
|
|
|
}
|