@ -1,5 +1,9 @@
|
|||||||
|
build/
|
||||||
|
openocd/
|
||||||
imgui.ini
|
imgui.ini
|
||||||
stmdspgui
|
stmdspgui
|
||||||
stmdspgui.exe
|
stmdspgui.exe
|
||||||
|
perf.data*
|
||||||
*.o
|
*.o
|
||||||
|
*.dll
|
||||||
.*
|
.*
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(stmdspgui VERSION 0.5)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
|
add_compile_options(-O0 -ggdb -g3)
|
||||||
|
|
||||||
|
file(GLOB SRC_IMGUI_BACKENDS "${CMAKE_SOURCE_DIR}/source/imgui/backends/*.cpp")
|
||||||
|
file(GLOB SRC_IMGUI "${CMAKE_SOURCE_DIR}/source/imgui/*.cpp")
|
||||||
|
file(GLOB SRC_STMDSP "${CMAKE_SOURCE_DIR}/source/stmdsp/*.cpp")
|
||||||
|
file(GLOB SRC_STMDSPGUI "${CMAKE_SOURCE_DIR}/source/*.cpp")
|
||||||
|
|
||||||
|
set_property(SOURCE ${SRC_STMDSPGUI} PROPERTY COMPILE_FLAGS "-Wall -Wextra -Wpedantic")
|
||||||
|
|
||||||
|
add_executable(stmdspgui
|
||||||
|
source/serial/src/serial.cc
|
||||||
|
source/serial/src/impl/unix.cc
|
||||||
|
source/serial/src/impl/list_ports/list_ports_linux.cc
|
||||||
|
${SRC_IMGUI_BACKENDS}
|
||||||
|
${SRC_IMGUI}
|
||||||
|
${SRC_STMDSP}
|
||||||
|
${SRC_STMDSPGUI})
|
||||||
|
|
||||||
|
target_include_directories(stmdspgui PUBLIC
|
||||||
|
${CMAKE_SOURCE_DIR}/source
|
||||||
|
${CMAKE_SOURCE_DIR}/source/imgui
|
||||||
|
${CMAKE_SOURCE_DIR}/source/stmdsp
|
||||||
|
${CMAKE_SOURCE_DIR}/source/serial/include)
|
||||||
|
|
||||||
|
target_link_libraries(stmdspgui PRIVATE SDL2 GL pthread)
|
||||||
|
|
@ -1,35 +1,46 @@
|
|||||||
|
CXX = g++
|
||||||
|
|
||||||
CXXFILES := \
|
CXXFILES := \
|
||||||
source/serial/src/serial.cc \
|
source/serial/src/serial.cc \
|
||||||
source/serial/src/impl/unix.cc \
|
|
||||||
source/serial/src/impl/list_ports/list_ports_linux.cc \
|
|
||||||
$(wildcard source/imgui/backends/*.cpp) \
|
$(wildcard source/imgui/backends/*.cpp) \
|
||||||
$(wildcard source/imgui/*.cpp) \
|
$(wildcard source/imgui/*.cpp) \
|
||||||
$(wildcard source/stmdsp/*.cpp) \
|
$(wildcard source/stmdsp/*.cpp) \
|
||||||
$(wildcard source/*.cpp)
|
$(wildcard source/*.cpp)
|
||||||
|
|
||||||
OFILES := $(patsubst %.cc, %.o, $(patsubst %.cpp, %.o, $(CXXFILES)))
|
CXXFLAGS := -std=c++20 -O2 \
|
||||||
|
-Isource -Isource/imgui -Isource/stmdsp -Isource/serial/include \
|
||||||
|
-Wall -Wextra -pedantic #-DSTMDSP_DISABLE_FORMULAS
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
CXXFILES += source/serial/src/impl/win.cc \
|
||||||
|
source/serial/src/impl/list_ports/list_ports_win.cc
|
||||||
|
CXXFLAGS += -DSTMDSP_WIN32 -Wa,-mbig-obj
|
||||||
|
LDFLAGS = -mwindows -lSDL2 -lopengl32 -lsetupapi -lole32
|
||||||
|
OUTPUT := stmdspgui.exe
|
||||||
|
else
|
||||||
|
CXXFILES += source/serial/src/impl/unix.cc \
|
||||||
|
source/serial/src/impl/list_ports/list_ports_linux.cc
|
||||||
|
LDFLAGS = -lSDL2 -lGL -lpthread
|
||||||
OUTPUT := stmdspgui
|
OUTPUT := stmdspgui
|
||||||
|
endif
|
||||||
|
|
||||||
#CXXFLAGS := -std=c++20 -O2 \
|
OFILES := $(patsubst %.cc, %.o, $(patsubst %.cpp, %.o, $(CXXFILES)))
|
||||||
# -Isource -Isource/imgui -Isource/stmdsp -Isource/serial/include
|
|
||||||
CXXFLAGS := -std=c++20 -ggdb -O0 -g3 \
|
|
||||||
-Isource -Isource/imgui -Isource/stmdsp -Isource/serial/include
|
|
||||||
|
|
||||||
all: $(OUTPUT)
|
all: $(OUTPUT)
|
||||||
|
|
||||||
$(OUTPUT): $(OFILES)
|
$(OUTPUT): $(OFILES)
|
||||||
@echo " LD " $(OUTPUT)
|
@echo " LD " $(OUTPUT)
|
||||||
@g++ $(OFILES) -o $(OUTPUT) -lSDL2 -lGL -lpthread
|
@$(CXX) $(OFILES) -o $(OUTPUT) $(LDFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@echo " CLEAN"
|
@echo " CLEAN"
|
||||||
@rm $(OFILES) $(OUTPUT)
|
@rm -f $(OFILES) $(OUTPUT)
|
||||||
|
|
||||||
%.o: %.cpp
|
%.o: %.cpp
|
||||||
@echo " CXX " $<
|
@echo " CXX " $<
|
||||||
@g++ $(CXXFLAGS) -c $< -o $@
|
@$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
%.o: %.cc
|
%.o: %.cc
|
||||||
@echo " CXX " $<
|
@echo " CXX " $<
|
||||||
@g++ $(CXXFLAGS) -c $< -o $@
|
@$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 6_iir_test.cpp
|
||||||
|
* Written by Clyne Sullivan.
|
||||||
|
*
|
||||||
|
* Implements a simple infinite impulse response (IIR) filter using an alpha
|
||||||
|
* parameter.
|
||||||
|
* To build upon this example, try setting `alpha` with a parameter knob:
|
||||||
|
* alpha = param1() / 4095.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
Sample* process_data(Samples samples)
|
||||||
|
{
|
||||||
|
constexpr float alpha = 0.7;
|
||||||
|
|
||||||
|
static Sample prev = 2048;
|
||||||
|
|
||||||
|
samples[0] = (1 - alpha) * samples[0] + alpha * prev;
|
||||||
|
for (unsigned int i = 1; i < SIZE; i++)
|
||||||
|
samples[i] = (1 - alpha) * samples[i] + alpha * samples[i - 1];
|
||||||
|
prev = samples[SIZE - 1];
|
||||||
|
|
||||||
|
return samples;
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
source [find interface/stlink.cfg]
|
||||||
|
source [find target/stm32l4x.cfg]
|
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* @file circular.hpp
|
||||||
|
* @brief Small utility for filling a buffer in a circular manner.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Clyne Sullivan
|
||||||
|
*
|
||||||
|
* Distributed under the GNU GPL v3 or later. You should have received a copy of
|
||||||
|
* the GNU General Public License along with this program.
|
||||||
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CIRCULAR_HPP
|
||||||
|
#define CIRCULAR_HPP
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
template<template<typename> class Container, typename T>
|
||||||
|
class CircularBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CircularBuffer(Container<T>& container) :
|
||||||
|
m_begin(std::begin(container)),
|
||||||
|
m_end(std::end(container)),
|
||||||
|
m_current(m_begin) {}
|
||||||
|
|
||||||
|
void put(const T& value) noexcept {
|
||||||
|
*m_current = value;
|
||||||
|
if (++m_current == m_end)
|
||||||
|
m_current = m_begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept {
|
||||||
|
return std::distance(m_begin, m_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(const T& fill) noexcept {
|
||||||
|
std::fill(m_begin, m_end, fill);
|
||||||
|
m_current = m_begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Container<T>::iterator m_begin;
|
||||||
|
Container<T>::iterator m_end;
|
||||||
|
Container<T>::iterator m_current;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CIRCULAR_HPP
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
#ifndef STMDSP_CONFIG_H
|
|
||||||
#define STMDSP_CONFIG_H
|
|
||||||
|
|
||||||
constexpr unsigned int WINDOW_WIDTH = 640;
|
|
||||||
constexpr unsigned int WINDOW_HEIGHT = 720;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,28 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* @file device_formula.cpp
|
||||||
|
* @brief Function for filling generator buffer using a mathematical formula.
|
||||||
|
* This is kept in its own file as exprtk.hpp takes forever to compile.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Clyne Sullivan
|
||||||
|
*
|
||||||
|
* Distributed under the GNU GPL v3 or later. You should have received a copy of
|
||||||
|
* the GNU General Public License along with this program.
|
||||||
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "stmdsp.hpp"
|
#include "stmdsp.hpp"
|
||||||
#include "exprtk.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string_view formulaString)
|
#ifndef STMDSP_DISABLE_FORMULAS
|
||||||
|
|
||||||
|
#define exprtk_disable_comments
|
||||||
|
#define exprtk_disable_break_continue
|
||||||
|
#define exprtk_disable_sc_andor
|
||||||
|
#define exprtk_disable_return_statement
|
||||||
|
#define exprtk_disable_enhanced_features
|
||||||
|
//#define exprtk_disable_string_capabilities
|
||||||
|
#define exprtk_disable_superscalar_unroll
|
||||||
|
#define exprtk_disable_rtl_io_file
|
||||||
|
#define exprtk_disable_rtl_vecops
|
||||||
|
//#define exprtk_disable_caseinsensitivity
|
||||||
|
#include "exprtk.hpp"
|
||||||
|
|
||||||
|
static std::random_device randomDevice;
|
||||||
|
|
||||||
|
std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string& formulaString)
|
||||||
{
|
{
|
||||||
double x = 0;
|
double x = 0;
|
||||||
|
|
||||||
exprtk::symbol_table<double> symbol_table;
|
exprtk::symbol_table<double> symbol_table;
|
||||||
|
exprtk::function_compositor<double> compositor (symbol_table);
|
||||||
exprtk::expression<double> expression;
|
exprtk::expression<double> expression;
|
||||||
exprtk::parser<double> parser;
|
exprtk::parser<double> parser;
|
||||||
|
|
||||||
symbol_table.add_variable("x", x);
|
|
||||||
symbol_table.add_constants();
|
symbol_table.add_constants();
|
||||||
|
symbol_table.add_variable("x", x);
|
||||||
|
symbol_table.add_function("random",
|
||||||
|
[](double l, double h) -> double {
|
||||||
|
return std::uniform_real_distribution<double>(l, h)(randomDevice);
|
||||||
|
});
|
||||||
|
compositor.add(exprtk::function_compositor<double>::function()
|
||||||
|
.name("square")
|
||||||
|
.var("X")
|
||||||
|
.expression("ceil(sin(pi*X))"));
|
||||||
|
compositor.add(exprtk::function_compositor<double>::function()
|
||||||
|
.name("triangle")
|
||||||
|
.var("X")
|
||||||
|
.expression("ceil(sin(pi*X))*(X-floor(X))+ceil(-sin(pi*X))*(-X-floor(-X))"));
|
||||||
|
compositor.add(exprtk::function_compositor<double>::function()
|
||||||
|
.name("pulse")
|
||||||
|
.var("L")
|
||||||
|
.var("X")
|
||||||
|
.expression("if(X<=L,1,0)"));
|
||||||
expression.register_symbol_table(symbol_table);
|
expression.register_symbol_table(symbol_table);
|
||||||
parser.compile(std::string(formulaString), expression);
|
parser.compile(formulaString, expression);
|
||||||
|
|
||||||
|
const auto genFun = [&x, &expression] {
|
||||||
|
const auto s = std::clamp(expression.value(), -1., 1.) * 2048. + 2048.;
|
||||||
|
++x;
|
||||||
|
return static_cast<stmdsp::dacsample_t>(std::min(s, 4095.));
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<stmdsp::dacsample_t> samples (stmdsp::SAMPLES_MAX);
|
std::vector<stmdsp::dacsample_t> samples (stmdsp::SAMPLES_MAX);
|
||||||
|
std::generate(samples.begin(), samples.end(), genFun);
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
std::generate(samples.begin(), samples.end(),
|
#else // no formula support
|
||||||
[&] { ++x; return static_cast<stmdsp::dacsample_t>(expression.value()); });
|
|
||||||
|
|
||||||
return samples;
|
std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string&)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // STMDSP_DISABLE_FORMULAS
|
||||||
|
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* @file code.cpp
|
||||||
|
* @brief Contains code for algorithm-code-related UI elements and logic.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Clyne Sullivan
|
||||||
|
*
|
||||||
|
* Distributed under the GNU GPL v3 or later. You should have received a copy of
|
||||||
|
* the GNU General Public License along with this program.
|
||||||
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "backends/imgui_impl_sdl.h"
|
||||||
|
#include "backends/imgui_impl_opengl2.h"
|
||||||
|
#include "TextEditor.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern void compileEditorCode(const std::string& code);
|
||||||
|
extern void disassembleCode();
|
||||||
|
|
||||||
|
TextEditor editor; // file.cpp
|
||||||
|
|
||||||
|
static std::string editorCompiled;
|
||||||
|
|
||||||
|
static void codeCompile();
|
||||||
|
static void codeDisassemble();
|
||||||
|
|
||||||
|
void codeEditorInit()
|
||||||
|
{
|
||||||
|
editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
|
||||||
|
editor.SetPalette(TextEditor::GetLightPalette());
|
||||||
|
}
|
||||||
|
|
||||||
|
void codeRenderMenu()
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Code")) {
|
||||||
|
if (ImGui::MenuItem("Compile"))
|
||||||
|
codeCompile();
|
||||||
|
if (ImGui::MenuItem("Disassemble"))
|
||||||
|
codeDisassemble();
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void codeRenderToolbar()
|
||||||
|
{
|
||||||
|
if (ImGui::Button("Compile"))
|
||||||
|
codeCompile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void codeRenderWidgets(const ImVec2& size)
|
||||||
|
{
|
||||||
|
editor.Render("code", size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void codeCompile()
|
||||||
|
{
|
||||||
|
compileEditorCode(editor.GetText());
|
||||||
|
editorCompiled = editor.GetText().compare(editorCompiled);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void codeDisassemble()
|
||||||
|
{
|
||||||
|
if (editor.GetText().compare(editorCompiled) != 0)
|
||||||
|
codeCompile();
|
||||||
|
disassembleCode();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,418 @@
|
|||||||
|
#include "circular.hpp"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
#include "ImGuiFileDialog.h"
|
||||||
|
|
||||||
|
#include "stmdsp.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
// Used for status queries and buffer size configuration.
|
||||||
|
extern std::shared_ptr<stmdsp::device> m_device;
|
||||||
|
|
||||||
|
void deviceAlgorithmUnload();
|
||||||
|
void deviceAlgorithmUpload();
|
||||||
|
bool deviceConnect();
|
||||||
|
void deviceGenLoadFormula(const std::string& list);
|
||||||
|
void deviceGenLoadList(std::string_view list);
|
||||||
|
bool deviceGenStartToggle();
|
||||||
|
void deviceLoadAudioFile(const std::string& file);
|
||||||
|
void deviceLoadLogFile(const std::string& file);
|
||||||
|
void deviceSetSampleRate(unsigned int index);
|
||||||
|
void deviceSetInputDrawing(bool enabled);
|
||||||
|
void deviceStart(bool logResults, bool drawSamples);
|
||||||
|
void deviceStartMeasurement();
|
||||||
|
void deviceUpdateDrawBufferSize(double timeframe);
|
||||||
|
std::size_t pullFromDrawQueue(
|
||||||
|
CircularBuffer<std::vector, stmdsp::dacsample_t>& circ);
|
||||||
|
std::size_t pullFromInputDrawQueue(
|
||||||
|
CircularBuffer<std::vector, stmdsp::dacsample_t>& circ);
|
||||||
|
|
||||||
|
static std::string sampleRatePreview = "?";
|
||||||
|
static bool measureCodeTime = false;
|
||||||
|
static bool logResults = false;
|
||||||
|
static bool drawSamples = false;
|
||||||
|
static bool popupRequestBuffer = false;
|
||||||
|
static bool popupRequestSiggen = false;
|
||||||
|
static bool popupRequestLog = false;
|
||||||
|
static double drawSamplesTimeframe = 1.0; // seconds
|
||||||
|
|
||||||
|
static std::string getSampleRatePreview(unsigned int rate)
|
||||||
|
{
|
||||||
|
return std::to_string(rate / 1000) + " kHz";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string connectLabel ("Connect");
|
||||||
|
void deviceRenderDisconnect()
|
||||||
|
{
|
||||||
|
connectLabel = "Connect";
|
||||||
|
measureCodeTime = false;
|
||||||
|
logResults = false;
|
||||||
|
drawSamples = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deviceRenderMenu()
|
||||||
|
{
|
||||||
|
auto addMenuItem = [](const std::string& label, bool enable, auto action) {
|
||||||
|
if (ImGui::MenuItem(label.c_str(), nullptr, false, enable)) {
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ImGui::BeginMenu("Device")) {
|
||||||
|
addMenuItem(connectLabel, !m_device || !m_device->is_running(), [&] {
|
||||||
|
if (deviceConnect()) {
|
||||||
|
connectLabel = "Disconnect";
|
||||||
|
sampleRatePreview =
|
||||||
|
getSampleRatePreview(m_device->get_sample_rate());
|
||||||
|
deviceUpdateDrawBufferSize(drawSamplesTimeframe);
|
||||||
|
} else {
|
||||||
|
deviceRenderDisconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bool isConnected = m_device ? true : false;
|
||||||
|
const bool isRunning = isConnected && m_device->is_running();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
static std::string startLabel ("Start");
|
||||||
|
addMenuItem(startLabel, isConnected, [&] {
|
||||||
|
startLabel = isRunning ? "Start" : "Stop";
|
||||||
|
deviceStart(logResults, drawSamples);
|
||||||
|
if (logResults && isRunning)
|
||||||
|
logResults = false;
|
||||||
|
});
|
||||||
|
addMenuItem("Upload algorithm", isConnected && !isRunning,
|
||||||
|
deviceAlgorithmUpload);
|
||||||
|
addMenuItem("Unload algorithm", isConnected && !isRunning,
|
||||||
|
deviceAlgorithmUnload);
|
||||||
|
addMenuItem("Measure Code Time", isRunning, deviceStartMeasurement);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if (!isConnected || isRunning)
|
||||||
|
ImGui::PushDisabled(); // Hey, pushing disabled!
|
||||||
|
|
||||||
|
ImGui::Checkbox("Draw samples", &drawSamples);
|
||||||
|
if (ImGui::Checkbox("Log results...", &logResults)) {
|
||||||
|
if (logResults)
|
||||||
|
popupRequestLog = true;
|
||||||
|
}
|
||||||
|
addMenuItem("Set buffer size...", true, [] { popupRequestBuffer = true; });
|
||||||
|
|
||||||
|
if (!isConnected || isRunning)
|
||||||
|
ImGui::PopDisabled();
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
addMenuItem("Load signal generator",
|
||||||
|
isConnected && !m_device->is_siggening() && !m_device->is_running(),
|
||||||
|
[] { popupRequestSiggen = true; });
|
||||||
|
|
||||||
|
static std::string startSiggenLabel ("Start signal generator");
|
||||||
|
addMenuItem(startSiggenLabel, isConnected, [&] {
|
||||||
|
const bool running = deviceGenStartToggle();
|
||||||
|
startSiggenLabel = running ? "Stop signal generator"
|
||||||
|
: "Start signal generator";
|
||||||
|
});
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void deviceRenderToolbar()
|
||||||
|
{
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Upload"))
|
||||||
|
deviceAlgorithmUpload();
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(100);
|
||||||
|
|
||||||
|
const bool enable =
|
||||||
|
m_device && !m_device->is_running() && !m_device->is_siggening();
|
||||||
|
if (!enable)
|
||||||
|
ImGui::PushDisabled();
|
||||||
|
|
||||||
|
if (ImGui::BeginCombo("", sampleRatePreview.c_str())) {
|
||||||
|
extern std::array<unsigned int, 6> sampleRateInts;
|
||||||
|
|
||||||
|
for (const auto& r : sampleRateInts) {
|
||||||
|
const auto s = getSampleRatePreview(r);
|
||||||
|
if (ImGui::Selectable(s.c_str())) {
|
||||||
|
sampleRatePreview = s;
|
||||||
|
deviceSetSampleRate(r);
|
||||||
|
deviceUpdateDrawBufferSize(drawSamplesTimeframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enable)
|
||||||
|
ImGui::PopDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deviceRenderWidgets()
|
||||||
|
{
|
||||||
|
static std::string siggenInput (32768, '\0');
|
||||||
|
static int siggenOption = 0;
|
||||||
|
|
||||||
|
if (popupRequestSiggen) {
|
||||||
|
popupRequestSiggen = false;
|
||||||
|
ImGui::OpenPopup("siggen");
|
||||||
|
} else if (popupRequestBuffer) {
|
||||||
|
popupRequestBuffer = false;
|
||||||
|
ImGui::OpenPopup("buffer");
|
||||||
|
} else if (popupRequestLog) {
|
||||||
|
popupRequestLog = false;
|
||||||
|
ImGuiFileDialog::Instance()->OpenModal(
|
||||||
|
"ChooseFileLog", "Choose File", ".csv", ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginPopup("siggen")) {
|
||||||
|
if (ImGui::RadioButton("List", &siggenOption, 0)) {
|
||||||
|
siggenInput.resize(32768);
|
||||||
|
siggenInput[0] = '\0';
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Formula", &siggenOption, 1)) {
|
||||||
|
siggenInput.resize(1024);
|
||||||
|
siggenInput[0] = '\0';
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Audio File", &siggenOption, 2))
|
||||||
|
siggenInput.clear();
|
||||||
|
|
||||||
|
if (siggenOption == 2) {
|
||||||
|
if (ImGui::Button("Choose File")) {
|
||||||
|
// This dialog will override the siggen popup, closing it.
|
||||||
|
ImGuiFileDialog::Instance()->OpenModal(
|
||||||
|
"ChooseFileGen", "Choose File", ".wav", ".");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGui::Text(siggenOption == 0 ? "Enter a list of numbers:"
|
||||||
|
: "Enter a formula. x = sample #, y = -1 to 1.\nf(x) = ");
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, {.8, .8, .8, 1});
|
||||||
|
ImGui::InputText("", siggenInput.data(), siggenInput.size());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Save")) {
|
||||||
|
switch (siggenOption) {
|
||||||
|
case 0:
|
||||||
|
deviceGenLoadList(siggenInput.substr(0, siggenInput.find('\0')));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
deviceGenLoadFormula(siggenInput.substr(0, siggenInput.find('\0')));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Cancel")) {
|
||||||
|
siggenInput.clear();
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginPopup("buffer")) {
|
||||||
|
static std::string bufferSizeInput ("4096");
|
||||||
|
ImGui::Text("Please enter a new sample buffer size (100-4096):");
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, {.8, .8, .8, 1});
|
||||||
|
ImGui::InputText("",
|
||||||
|
bufferSizeInput.data(),
|
||||||
|
bufferSizeInput.size(),
|
||||||
|
ImGuiInputTextFlags_CharsDecimal);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
if (ImGui::Button("Save")) {
|
||||||
|
if (m_device) {
|
||||||
|
int n = std::clamp(std::stoi(bufferSizeInput), 100, 4096);
|
||||||
|
m_device->continuous_set_buffer_size(n);
|
||||||
|
}
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Cancel"))
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGuiFileDialog::Instance()->Display("ChooseFileLog",
|
||||||
|
ImGuiWindowFlags_NoCollapse,
|
||||||
|
ImVec2(460, 540)))
|
||||||
|
{
|
||||||
|
if (ImGuiFileDialog::Instance()->IsOk()) {
|
||||||
|
const auto filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
|
||||||
|
deviceLoadLogFile(filePathName);
|
||||||
|
} else {
|
||||||
|
logResults = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiFileDialog::Instance()->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGuiFileDialog::Instance()->Display("ChooseFileGen",
|
||||||
|
ImGuiWindowFlags_NoCollapse,
|
||||||
|
ImVec2(460, 540)))
|
||||||
|
{
|
||||||
|
if (ImGuiFileDialog::Instance()->IsOk()) {
|
||||||
|
const auto filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
|
||||||
|
deviceLoadAudioFile(filePathName);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiFileDialog::Instance()->Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void deviceRenderDraw()
|
||||||
|
{
|
||||||
|
if (drawSamples) {
|
||||||
|
static std::vector<stmdsp::dacsample_t> buffer;
|
||||||
|
static std::vector<stmdsp::dacsample_t> bufferInput;
|
||||||
|
static auto bufferCirc = CircularBuffer(buffer);
|
||||||
|
static auto bufferInputCirc = CircularBuffer(bufferInput);
|
||||||
|
|
||||||
|
static bool drawSamplesInput = false;
|
||||||
|
static unsigned int yMinMax = 4095;
|
||||||
|
|
||||||
|
ImGui::Begin("draw", &drawSamples);
|
||||||
|
ImGui::Text("Draw input ");
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Checkbox("", &drawSamplesInput)) {
|
||||||
|
deviceSetInputDrawing(drawSamplesInput);
|
||||||
|
if (drawSamplesInput) {
|
||||||
|
bufferCirc.reset(2048);
|
||||||
|
bufferInputCirc.reset(2048);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("Time: %0.3f sec", drawSamplesTimeframe);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("-", {30, 0})) {
|
||||||
|
drawSamplesTimeframe = std::max(drawSamplesTimeframe / 2., 0.0078125);
|
||||||
|
deviceUpdateDrawBufferSize(drawSamplesTimeframe);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("+", {30, 0})) {
|
||||||
|
drawSamplesTimeframe = std::min(drawSamplesTimeframe * 2, 32.);
|
||||||
|
deviceUpdateDrawBufferSize(drawSamplesTimeframe);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("Y: +/-%1.2fV", 3.3f * (static_cast<float>(yMinMax) / 4095.f));
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(" - ", {30, 0})) {
|
||||||
|
yMinMax = std::max(63u, yMinMax >> 1);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(" + ", {30, 0})) {
|
||||||
|
yMinMax = std::min(4095u, (yMinMax << 1) | 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newSize = pullFromDrawQueue(bufferCirc);
|
||||||
|
if (newSize > 0) {
|
||||||
|
buffer.resize(newSize);
|
||||||
|
bufferCirc = CircularBuffer(buffer);
|
||||||
|
pullFromDrawQueue(bufferCirc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawSamplesInput) {
|
||||||
|
auto newSize = pullFromInputDrawQueue(bufferInputCirc);
|
||||||
|
if (newSize > 0) {
|
||||||
|
bufferInput.resize(newSize);
|
||||||
|
bufferInputCirc = CircularBuffer(bufferInput);
|
||||||
|
pullFromInputDrawQueue(bufferInputCirc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto drawList = ImGui::GetWindowDrawList();
|
||||||
|
ImVec2 p0 = ImGui::GetWindowPos();
|
||||||
|
auto size = ImGui::GetWindowSize();
|
||||||
|
p0.y += 65;
|
||||||
|
size.y -= 70;
|
||||||
|
drawList->AddRectFilled(p0, {p0.x + size.x, p0.y + size.y}, IM_COL32_BLACK);
|
||||||
|
|
||||||
|
const auto lcMinor = ImGui::GetColorU32(IM_COL32(40, 40, 40, 255));
|
||||||
|
const auto lcMajor = ImGui::GetColorU32(IM_COL32(140, 140, 140, 255));
|
||||||
|
|
||||||
|
{
|
||||||
|
const float yinc = (3. / 3.3) * size.y / 12.f;
|
||||||
|
const float center = p0.y + size.y / 2;
|
||||||
|
drawList->AddLine({p0.x, center}, {p0.x + size.x, center}, ImGui::GetColorU32(IM_COL32_WHITE));
|
||||||
|
for (int i = 1; i < 7; ++i) {
|
||||||
|
drawList->AddLine({p0.x, center + i * yinc}, {p0.x + size.x, center + i * yinc}, (i % 2) ? lcMinor : lcMajor);
|
||||||
|
drawList->AddLine({p0.x, center - i * yinc}, {p0.x + size.x, center - i * yinc}, (i % 2) ? lcMinor : lcMajor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const float xinc = size.x / 16.f;
|
||||||
|
const float center = p0.x + size.x / 2;
|
||||||
|
drawList->AddLine({center, p0.y}, {center, p0.y + size.y}, ImGui::GetColorU32(IM_COL32_WHITE));
|
||||||
|
for (int i = 1; i < 8; ++i) {
|
||||||
|
drawList->AddLine({center + i * xinc, p0.y}, {center + i * xinc, p0.y + size.y}, (i % 2) ? lcMinor : lcMajor);
|
||||||
|
drawList->AddLine({center - i * xinc, p0.y}, {center - i * xinc, p0.y + size.y}, (i % 2) ? lcMinor : lcMajor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const float di = static_cast<float>(buffer.size()) / size.x;
|
||||||
|
const float dx = std::ceil(size.x / static_cast<float>(buffer.size()));
|
||||||
|
ImVec2 pp = p0;
|
||||||
|
float i = 0;
|
||||||
|
while (pp.x < p0.x + size.x) {
|
||||||
|
unsigned int idx = i;
|
||||||
|
float n = std::clamp((buffer[idx] - 2048.) / yMinMax, -0.5, 0.5);
|
||||||
|
i += di;
|
||||||
|
|
||||||
|
ImVec2 next (pp.x + dx, p0.y + size.y * (0.5 - n));
|
||||||
|
drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(255, 0, 0, 255)));
|
||||||
|
pp = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawSamplesInput) {
|
||||||
|
ImVec2 pp = p0;
|
||||||
|
float i = 0;
|
||||||
|
while (pp.x < p0.x + size.x) {
|
||||||
|
unsigned int idx = i;
|
||||||
|
float n = std::clamp((bufferInput[idx] - 2048.) / yMinMax, -0.5, 0.5);
|
||||||
|
i += di;
|
||||||
|
|
||||||
|
ImVec2 next (pp.x + dx, p0.y + size.y * (0.5 - n));
|
||||||
|
drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(0, 0, 255, 255)));
|
||||||
|
pp = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto mouse = ImGui::GetMousePos();
|
||||||
|
if (mouse.x > p0.x && mouse.x < p0.x + size.x &&
|
||||||
|
mouse.y > p0.y && mouse.y < p0.y + size.y)
|
||||||
|
{
|
||||||
|
char buf[16];
|
||||||
|
drawList->AddLine({mouse.x, p0.y}, {mouse.x, p0.y + size.y}, IM_COL32(255, 255, 0, 255));
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::size_t si = (mouse.x - p0.x) / size.x * buffer.size();
|
||||||
|
const float s = buffer[si] / 4095.f * 6.6f - 3.3f;
|
||||||
|
snprintf(buf, sizeof(buf), " %1.3fV", s);
|
||||||
|
drawList->AddText(mouse, IM_COL32(255, 0, 0, 255), buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawSamplesInput) {
|
||||||
|
const std::size_t si = (mouse.x - p0.x) / size.x * bufferInput.size();
|
||||||
|
const float s = bufferInput[si] / 4095.f * 6.6f - 3.3f;
|
||||||
|
snprintf(buf, sizeof(buf), " %1.3fV", s);
|
||||||
|
drawList->AddText({mouse.x, mouse.y + 20}, IM_COL32(0, 0, 255, 255), buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
|||||||
|
#include "imgui.h"
|
||||||
|
#include "ImGuiFileDialog.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
void log(const std::string& str);
|
||||||
|
|
||||||
|
static bool showDownloadFirmware = false;
|
||||||
|
static bool showHelp = false;
|
||||||
|
static std::string firmwareFile;
|
||||||
|
|
||||||
|
static void helpDownloadThread();
|
||||||
|
|
||||||
|
void helpRenderMenu()
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Help")) {
|
||||||
|
if (ImGui::MenuItem("Open wiki...")) {
|
||||||
|
#ifdef STMDSP_WIN32
|
||||||
|
system("start "
|
||||||
|
#else
|
||||||
|
system("xdg-open "
|
||||||
|
#endif
|
||||||
|
"https://code.bitgloo.com/clyne/stmdspgui/wiki");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Download firmware...")) {
|
||||||
|
showDownloadFirmware = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if (ImGui::MenuItem("About")) {
|
||||||
|
showHelp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void helpRenderDialog()
|
||||||
|
{
|
||||||
|
if (showDownloadFirmware) {
|
||||||
|
showDownloadFirmware = false;
|
||||||
|
ImGuiFileDialog::Instance()->OpenModal(
|
||||||
|
"ChooseFileFW", "Choose Firmware File", ".hex", ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGuiFileDialog::Instance()->Display("ChooseFileFW",
|
||||||
|
ImGuiWindowFlags_NoCollapse,
|
||||||
|
ImVec2(460, 540)))
|
||||||
|
{
|
||||||
|
if (ImGuiFileDialog::Instance()->IsOk()) {
|
||||||
|
firmwareFile = ImGuiFileDialog::Instance()->GetFilePathName();
|
||||||
|
#ifdef STMDSP_WIN32
|
||||||
|
size_t i = 0;
|
||||||
|
while ((i = firmwareFile.find('\\', i)) != std::string::npos) {
|
||||||
|
firmwareFile.replace(i, 1, "\\\\");
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::thread(helpDownloadThread).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiFileDialog::Instance()->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showHelp) {
|
||||||
|
ImGui::Begin("help");
|
||||||
|
|
||||||
|
ImGui::Text("stmdspgui\nCompiled on " __DATE__ ".\n\nWritten by Clyne Sullivan.\n");
|
||||||
|
|
||||||
|
if (ImGui::Button("Close")) {
|
||||||
|
showHelp = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!firmwareFile.empty()) {
|
||||||
|
ImGui::Begin("Downloading");
|
||||||
|
|
||||||
|
ImGui::Text("Downloading firmware to device...");
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void helpDownloadThread()
|
||||||
|
{
|
||||||
|
std::string command (
|
||||||
|
#ifdef STMDSP_WIN32
|
||||||
|
"openocd\\bin\\openocd.exe"
|
||||||
|
#else
|
||||||
|
"openocd"
|
||||||
|
#endif
|
||||||
|
" -f openocd.cfg -c \"program $0 reset exit\"");
|
||||||
|
|
||||||
|
command.replace(command.find("$0"), 2, firmwareFile);
|
||||||
|
|
||||||
|
std::cout << "Run: " << command << std::endl;
|
||||||
|
|
||||||
|
if (system(command.c_str()) == 0) {
|
||||||
|
log("Programming finished.");
|
||||||
|
} else {
|
||||||
|
log("Error while programming device!");
|
||||||
|
}
|
||||||
|
|
||||||
|
firmwareFile.clear();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
|||||||
|
#include "logview.h"
|
||||||
|
|
||||||
|
LogView::LogView()
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogView::Clear()
|
||||||
|
{
|
||||||
|
Buf.clear();
|
||||||
|
LineOffsets.clear();
|
||||||
|
LineOffsets.push_back(0);
|
||||||
|
updated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogView::AddLog(const std::string& str)
|
||||||
|
{
|
||||||
|
AddLog(str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogView::AddLog(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
int old_size = Buf.size();
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Buf.appendfv(fmt, args);
|
||||||
|
Buf.appendfv("\n", args);
|
||||||
|
va_end(args);
|
||||||
|
for (int new_size = Buf.size(); old_size < new_size; old_size++)
|
||||||
|
if (Buf[old_size] == '\n')
|
||||||
|
LineOffsets.push_back(old_size + 1);
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogView::Draw(const char* title, bool* p_open, ImGuiWindowFlags flags)
|
||||||
|
{
|
||||||
|
if (!ImGui::Begin(title, p_open, flags))
|
||||||
|
{
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Log ");
|
||||||
|
ImGui::SameLine(ImGui::GetWindowWidth() - 120);
|
||||||
|
if (ImGui::Button("Clear"))
|
||||||
|
Clear();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Copy"))
|
||||||
|
ImGui::LogToClipboard();
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
|
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||||
|
const char* buf = Buf.begin();
|
||||||
|
const char* buf_end = Buf.end();
|
||||||
|
|
||||||
|
ImGuiListClipper clipper;
|
||||||
|
clipper.Begin(LineOffsets.Size);
|
||||||
|
|
||||||
|
while (clipper.Step())
|
||||||
|
{
|
||||||
|
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
||||||
|
{
|
||||||
|
const char* line_start = buf + LineOffsets[line_no];
|
||||||
|
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
||||||
|
ImGui::TextUnformatted(line_start, line_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clipper.End();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
ImGui::SetScrollHereY();
|
||||||
|
updated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
Sample *process_data(Samples samples)
|
|
||||||
{
|
|
||||||
constexpr float alpha = 0.7;
|
|
||||||
|
|
||||||
static Sample prev = 2048;
|
|
||||||
|
|
||||||
samples[0] = (1 - alpha) * samples[0] + alpha * prev;
|
|
||||||
for (unsigned int i = 1; i < samples.size(); i++)
|
|
||||||
samples[i] = (1 - alpha) * samples[i] + alpha * samples[i - 1];
|
|
||||||
prev = samples[samples.size() - 1];
|
|
||||||
|
|
||||||
return samples.data();
|
|
||||||
}
|
|
Loading…
Reference in New Issue