code cleanup; fix audio streaming

pull/1/head
Clyne 3 years ago
parent 4306970fd3
commit f6318e3284

@ -33,6 +33,8 @@ std::string tempFileName; // device.cpp
static std::string editorCompiled; static std::string editorCompiled;
static std::string newTempFileName(); static std::string newTempFileName();
static bool codeExecuteCommand(const std::string& command, const std::string& file);
static void stringReplaceAll(std::string& str, const std::string& what, const std::string& with);
static void compileEditorCode(); static void compileEditorCode();
static void disassembleCode(); static void disassembleCode();
@ -64,6 +66,39 @@ void codeRenderWidgets()
editor.Render("code", {WINDOW_WIDTH - 15, 450}, true); editor.Render("code", {WINDOW_WIDTH - 15, 450}, true);
} }
std::string newTempFileName()
{
const auto path = std::filesystem::temp_directory_path() / "stmdspgui_build";
return path.string();
}
bool codeExecuteCommand(const std::string& command, const std::string& file)
{
if (system(command.c_str()) == 0) {
if (std::ifstream output (file); output.good()) {
std::ostringstream sstr;
sstr << output.rdbuf();
log(sstr.str().c_str());
} else {
log("Could not read command output!");
}
std::filesystem::remove(file);
return true;
} else {
return false;
}
}
void stringReplaceAll(std::string& str, const std::string& what, const std::string& with)
{
std::size_t i;
while ((i = str.find(what)) != std::string::npos) {
str.replace(i, what.size(), with);
i += what.size();
}
};
void compileEditorCode() void compileEditorCode()
{ {
log("Compiling..."); log("Compiling...");
@ -74,35 +109,28 @@ void compileEditorCode()
std::filesystem::remove(tempFileName + ".orig.o"); std::filesystem::remove(tempFileName + ".orig.o");
} }
stmdsp::platform platform; const auto platform = m_device ? m_device->get_platform()
if (m_device) { : stmdsp::platform::L4;
platform = m_device->get_platform();
} else {
// Assume a default.
platform = stmdsp::platform::L4;
}
if (tempFileName.size() == 0) if (tempFileName.empty())
tempFileName = newTempFileName(); tempFileName = newTempFileName();
{ {
std::ofstream file (tempFileName, std::ios::trunc | std::ios::binary); std::ofstream file (tempFileName, std::ios::trunc | std::ios::binary);
auto file_text = platform == stmdsp::platform::L4 ? stmdsp::file_header_l4 auto file_text =
platform == stmdsp::platform::L4 ? stmdsp::file_header_l4
: stmdsp::file_header_h7; : stmdsp::file_header_h7;
auto samples_text = std::to_string(m_device ? m_device->get_buffer_size() const auto buffer_size = m_device ? m_device->get_buffer_size()
: stmdsp::SAMPLES_MAX); : stmdsp::SAMPLES_MAX;
for (std::size_t i = 0; (i = file_text.find("$0", i)) != std::string::npos;) {
file_text.replace(i, 2, samples_text);
i += 2;
}
file << file_text; stringReplaceAll(file_text, "$0", std::to_string(buffer_size));
file << "\n";
file << editor.GetText(); file << file_text << '\n' << editor.GetText();
} }
constexpr const char *script_ext = const auto scriptFile = tempFileName +
#ifndef STMDSP_WIN32 #ifndef STMDSP_WIN32
".sh"; ".sh";
#else #else
@ -110,44 +138,33 @@ void compileEditorCode()
#endif #endif
{ {
std::ofstream makefile (tempFileName + script_ext, std::ios::binary); std::ofstream makefile (scriptFile, std::ios::binary);
auto make_text = platform == stmdsp::platform::L4 ? stmdsp::makefile_text_l4 auto make_text =
platform == stmdsp::platform::L4 ? stmdsp::makefile_text_l4
: stmdsp::makefile_text_h7; : stmdsp::makefile_text_h7;
auto cwd = std::filesystem::current_path().string();
for (std::size_t i = 0; (i = make_text.find("$0", i)) != std::string::npos;) { stringReplaceAll(make_text, "$0", tempFileName);
make_text.replace(i, 2, tempFileName); stringReplaceAll(make_text, "$1",
i += 2; std::filesystem::current_path().string());
}
for (std::size_t i = 0; (i = make_text.find("$1", i)) != std::string::npos;) {
make_text.replace(i, 2, cwd);
i += 2;
}
makefile << make_text; makefile << make_text;
} }
auto makeOutput = tempFileName + script_ext + ".log";
auto makeCommand = tempFileName + script_ext + " > " + makeOutput + " 2>&1";
#ifndef STMDSP_WIN32 #ifndef STMDSP_WIN32
system((std::string("chmod +x ") + tempFileName + script_ext).c_str()); system((std::string("chmod +x ") + scriptFile).c_str());
#endif #endif
int result = system(makeCommand.c_str());
std::ifstream result_file (makeOutput);
std::ostringstream sstr;
sstr << result_file.rdbuf();
log(sstr.str().c_str());
std::filesystem::remove(tempFileName);
std::filesystem::remove(tempFileName + script_ext);
std::filesystem::remove(makeOutput);
if (result == 0) { const auto makeOutput = scriptFile + ".log";
const auto makeCommand = scriptFile + " > " + makeOutput + " 2>&1";
if (codeExecuteCommand(makeCommand, makeOutput)) {
editorCompiled = editor.GetText(); editorCompiled = editor.GetText();
log("Compilation succeeded."); log("Compilation succeeded.");
} else { } else {
log("Compilation failed."); log("Compilation failed.");
} }
std::filesystem::remove(tempFileName);
std::filesystem::remove(scriptFile);
} }
void disassembleCode() void disassembleCode()
@ -158,31 +175,15 @@ void disassembleCode()
compileEditorCode(); compileEditorCode();
} }
auto output = tempFileName + ".asm.log"; const auto output = tempFileName + ".asm.log";
auto command = std::string("arm-none-eabi-objdump -d --no-show-raw-insn ") + const auto command =
std::string("arm-none-eabi-objdump -d --no-show-raw-insn ") +
tempFileName + ".orig.o > " + output + " 2>&1"; tempFileName + ".orig.o > " + output + " 2>&1";
if (system(command.c_str()) == 0) { if (codeExecuteCommand(command, output)) {
{
std::ifstream result_file (output);
std::ostringstream sstr;
sstr << result_file.rdbuf();
log(sstr.str().c_str());
}
ImGui::OpenPopup("compile");
std::filesystem::remove(output);
log("Ready."); log("Ready.");
} else { } else {
log("Failed to load disassembly."); log("Failed to load disassembly.");
} }
} }
std::string newTempFileName()
{
auto tempPath = std::filesystem::temp_directory_path();
tempPath /= "stmdspgui_build";
return tempPath.string();
}

@ -9,11 +9,6 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
/**
* TODO list:
* - Improve signal generator audio streaming.
*/
#include "stmdsp.hpp" #include "stmdsp.hpp"
#include "imgui.h" #include "imgui.h"
@ -80,12 +75,42 @@ static unsigned int drawSamplesBufferSize = 1;
static void measureCodeTask(std::shared_ptr<stmdsp::device> device) static void measureCodeTask(std::shared_ptr<stmdsp::device> device)
{ {
if (!device) std::this_thread::sleep_for(std::chrono::seconds(1));
return;
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); if (device) {
auto cycles = device->continuous_start_get_measurement(); const auto cycles = device->continuous_start_get_measurement();
log(std::string("Execution time: ") + std::to_string(cycles) + " cycles."); log(std::string("Execution time: ") + std::to_string(cycles) + " cycles.");
} }
}
static std::vector<stmdsp::dacsample_t> tryReceiveChunk(
std::shared_ptr<stmdsp::device> device,
auto readFunc)
{
int tries = -1;
do {
const auto chunk = readFunc(device.get());
if (!chunk.empty())
return chunk;
else
std::this_thread::sleep_for(std::chrono::microseconds(20));
} while (++tries < 100 && device->is_running());
return {};
}
static std::chrono::duration<double> getBufferPeriod(
std::shared_ptr<stmdsp::device> device,
const double factor = 0.975)
{
if (device) {
const double bufferSize = device->get_buffer_size();
const double sampleRate = sampleRateInts[device->get_sample_rate()];
return std::chrono::duration<double>(bufferSize / sampleRate * factor);
} else {
return {};
}
}
static void drawSamplesTask(std::shared_ptr<stmdsp::device> device) static void drawSamplesTask(std::shared_ptr<stmdsp::device> device)
{ {
@ -93,69 +118,43 @@ static void drawSamplesTask(std::shared_ptr<stmdsp::device> device)
return; return;
const bool doLogger = logResults && logSamplesFile.good(); const bool doLogger = logResults && logSamplesFile.good();
const auto bufferTime = getBufferPeriod(device);
const double bufferSize = m_device->get_buffer_size();
const double sampleRate = sampleRateInts[m_device->get_sample_rate()];
unsigned long bufferTime = bufferSize / sampleRate * 0.975 * 1e6;
std::unique_lock<std::timed_mutex> lockDraw (mutexDrawSamples, std::defer_lock); std::unique_lock<std::timed_mutex> lockDraw (mutexDrawSamples, std::defer_lock);
std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock); std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
while (m_device && m_device->is_running()) { auto addToQueue = [&lockDraw](auto& queue, const auto& chunk) {
auto next = std::chrono::high_resolution_clock::now() + lockDraw.lock();
std::chrono::microseconds(bufferTime); std::copy(chunk.cbegin(), chunk.cend(), std::back_inserter(queue));
lockDraw.unlock();
};
std::vector<stmdsp::dacsample_t> chunk; while (device && device->is_running()) {
const auto next = std::chrono::high_resolution_clock::now() + bufferTime;
if (lockDevice.try_lock_until(next)) { if (lockDevice.try_lock_until(next)) {
chunk = m_device->continuous_read(); const auto chunk = tryReceiveChunk(device,
int tries = -1; std::mem_fn(&stmdsp::device::continuous_read));
while (chunk.empty() && m_device->is_running()) {
if (++tries == 100)
break;
std::this_thread::sleep_for(std::chrono::microseconds(20));
chunk = m_device->continuous_read();
}
lockDevice.unlock(); lockDevice.unlock();
addToQueue(drawSamplesQueue, chunk);
if (doLogger) {
for (const auto& s : chunk)
logSamplesFile << s << '\n';
}
} else { } else {
// Cooldown. // Device must be busy, cooldown.
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
} }
if (drawSamplesInput && popupRequestDraw) { if (drawSamplesInput && popupRequestDraw) {
std::vector<stmdsp::dacsample_t> chunk2;
if (lockDevice.try_lock_for(std::chrono::milliseconds(1))) { if (lockDevice.try_lock_for(std::chrono::milliseconds(1))) {
chunk2 = m_device->continuous_read_input(); const auto chunk2 = tryReceiveChunk(device,
int tries = -1; std::mem_fn(&stmdsp::device::continuous_read_input));
while (chunk2.empty() && m_device->is_running()) {
if (++tries == 100)
break;
std::this_thread::sleep_for(std::chrono::microseconds(20));
chunk2 = m_device->continuous_read_input();
}
lockDevice.unlock(); lockDevice.unlock();
}
lockDraw.lock(); addToQueue(drawSamplesInputQueue, chunk2);
auto i = chunk2.cbegin();
for (const auto& s : chunk) {
drawSamplesQueue.push_back(s);
drawSamplesInputQueue.push_back(*i++);
}
lockDraw.unlock();
} else if (!doLogger) {
lockDraw.lock();
for (const auto& s : chunk)
drawSamplesQueue.push_back(s);
lockDraw.unlock();
} else {
lockDraw.lock();
for (const auto& s : chunk) {
drawSamplesQueue.push_back(s);
logSamplesFile << s << '\n';
} }
lockDraw.unlock();
} }
std::this_thread::sleep_until(next); std::this_thread::sleep_until(next);
@ -167,38 +166,38 @@ static void feedSigGenTask(std::shared_ptr<stmdsp::device> device)
if (!device) if (!device)
return; return;
const auto bufferSize = m_device->get_buffer_size(); const auto delay = getBufferPeriod(device);
const double sampleRate = sampleRateInts[m_device->get_sample_rate()]; const auto uploadDelay = getBufferPeriod(device, 0.001);
const unsigned long delay = bufferSize / sampleRate * 0.975 * 1e6;
std::vector<stmdsp::adcsample_t> wavBuf (bufferSize, 2048); std::vector<stmdsp::dacsample_t> wavBuf (device->get_buffer_size() * 2, 2048);
std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock); std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
lockDevice.lock(); lockDevice.lock();
// One (or both) of these freezes the device... device->siggen_upload(wavBuf.data(), wavBuf.size());
m_device->siggen_upload(wavBuf.data(), wavBuf.size()); wavBuf.resize(wavBuf.size() / 2);
//m_device->siggen_start(); device->siggen_start();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
lockDevice.unlock(); lockDevice.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(delay)); std::vector<int16_t> wavIntBuf (wavBuf.size());
return;
while (genRunning) { while (genRunning) {
auto next = std::chrono::high_resolution_clock::now() + const auto next = std::chrono::high_resolution_clock::now() + delay;
std::chrono::microseconds(delay);
auto src = reinterpret_cast<uint16_t *>(wavOutput.next(bufferSize)); wavOutput.next(wavIntBuf.data(), wavIntBuf.size());
for (auto& w : wavBuf) auto src = wavIntBuf.cbegin();
w = *src++ / 16 + 2048; std::generate(wavBuf.begin(), wavBuf.end(),
[&src] { return static_cast<stmdsp::dacsample_t>(*src++ / 16 + 2048); });
if (lockDevice.try_lock_until(next)) { lockDevice.lock();
m_device->siggen_upload(wavBuf.data(), wavBuf.size()); while (!device->siggen_upload(wavBuf.data(), wavBuf.size()))
std::this_thread::sleep_for(uploadDelay);
lockDevice.unlock(); lockDevice.unlock();
std::this_thread::sleep_until(next); std::this_thread::sleep_until(next);
} }
} }
}
static void statusTask(std::shared_ptr<stmdsp::device> device) static void statusTask(std::shared_ptr<stmdsp::device> device)
{ {
@ -208,16 +207,20 @@ static void statusTask(std::shared_ptr<stmdsp::device> device)
while (device->connected()) { while (device->connected()) {
std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock); std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
lockDevice.lock(); lockDevice.lock();
auto [status, error] = device->get_status(); const auto [status, error] = device->get_status();
lockDevice.unlock(); lockDevice.unlock();
if (error != stmdsp::Error::None) { if (error != stmdsp::Error::None) {
if (error == stmdsp::Error::NotIdle) { switch (error) {
case stmdsp::Error::NotIdle:
log("Error: Device already running..."); log("Error: Device already running...");
} else if (error == stmdsp::Error::ConversionAborted) { break;
case stmdsp::Error::ConversionAborted:
log("Error: Algorithm unloaded, a fault occurred!"); log("Error: Algorithm unloaded, a fault occurred!");
} else { break;
default:
log("Error: Device had an issue..."); log("Error: Device had an issue...");
break;
} }
} }
@ -383,13 +386,13 @@ void deviceRenderDraw()
drawSamplesBufferSize = std::round(sr * tf); drawSamplesBufferSize = std::round(sr * tf);
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text("Y-minmax: %u", yMinMax); ImGui::Text("Y: +/-%1.2fV", 3.3f * (static_cast<float>(yMinMax) / 4095.f));
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("--", {30, 0})) { if (ImGui::Button(" - ", {30, 0})) {
yMinMax = std::max(63u, yMinMax >> 1); yMinMax = std::max(63u, yMinMax >> 1);
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("++", {30, 0})) { if (ImGui::Button(" + ", {30, 0})) {
yMinMax = std::min(4095u, (yMinMax << 1) | 1); yMinMax = std::min(4095u, (yMinMax << 1) | 1);
} }
@ -508,7 +511,7 @@ void deviceRenderMenu()
popupRequestBuffer = true; popupRequestBuffer = true;
} }
ImGui::Separator(); ImGui::Separator();
if (ImGui::MenuItem("Load signal generator", nullptr, false, isConnected && !isRunning)) { if (ImGui::MenuItem("Load signal generator", nullptr, false, isConnected && !m_device->is_siggening())) {
popupRequestSiggen = true; popupRequestSiggen = true;
} }
static const char *startSiggenLabel = "Start signal generator"; static const char *startSiggenLabel = "Start signal generator";
@ -542,11 +545,14 @@ void deviceRenderToolbar()
deviceAlgorithmUpload(); deviceAlgorithmUpload();
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(100); ImGui::SetNextItemWidth(100);
const bool enable = m_device && !m_device->is_running() && !m_device->is_siggening();
if (!enable)
ImGui::PushDisabled();
if (ImGui::BeginCombo("", sampleRatePreview)) { if (ImGui::BeginCombo("", sampleRatePreview)) {
for (unsigned int i = 0; i < sampleRateList.size(); ++i) { for (unsigned int i = 0; i < sampleRateList.size(); ++i) {
if (ImGui::Selectable(sampleRateList[i])) { if (ImGui::Selectable(sampleRateList[i])) {
sampleRatePreview = sampleRateList[i]; sampleRatePreview = sampleRateList[i];
if (m_device && !m_device->is_running()) {
do { do {
m_device->set_sample_rate(i); m_device->set_sample_rate(i);
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
@ -555,9 +561,10 @@ void deviceRenderToolbar()
drawSamplesBufferSize = std::round(sampleRateInts[i] * drawSamplesTimeframe); drawSamplesBufferSize = std::round(sampleRateInts[i] * drawSamplesTimeframe);
} }
} }
}
ImGui::EndCombo(); ImGui::EndCombo();
} }
if (!enable)
ImGui::PopDisabled();
} }
void deviceConnect() void deviceConnect()
@ -566,7 +573,7 @@ void deviceConnect()
if (!m_device) { if (!m_device) {
stmdsp::scanner scanner; stmdsp::scanner scanner;
if (auto devices = scanner.scan(); devices.size() > 0) { if (auto devices = scanner.scan(); !devices.empty()) {
try { try {
m_device.reset(new stmdsp::device(devices.front())); m_device.reset(new stmdsp::device(devices.front()));
} catch (...) { } catch (...) {
@ -608,8 +615,7 @@ void deviceStart()
if (m_device->is_running()) { if (m_device->is_running()) {
{ {
std::scoped_lock lock (mutexDrawSamples); std::scoped_lock lock (mutexDrawSamples, mutexDeviceLoad);
std::scoped_lock lock2 (mutexDeviceLoad);
std::this_thread::sleep_for(std::chrono::microseconds(150)); std::this_thread::sleep_for(std::chrono::microseconds(150));
m_device->continuous_stop(); m_device->continuous_stop();
} }
@ -637,10 +643,9 @@ void deviceAlgorithmUpload()
if (!m_device) { if (!m_device) {
log("No device connected."); log("No device connected.");
return; return;
} } else if (m_device->is_running()) {
if (m_device->is_running())
return; return;
}
if (std::ifstream algo (tempFileName + ".o"); algo.good()) { if (std::ifstream algo (tempFileName + ".o"); algo.good()) {
std::ostringstream sstr; std::ostringstream sstr;
@ -658,41 +663,38 @@ void deviceAlgorithmUnload()
{ {
if (!m_device) { if (!m_device) {
log("No device connected."); log("No device connected.");
return; } else if (!m_device->is_running()) {
}
if (!m_device->is_running()) {
m_device->unload_filter(); m_device->unload_filter();
log("Algorithm unloaded."); log("Algorithm unloaded.");
} }
} }
void deviceGenLoadList(std::string_view listStr) void deviceGenLoadList(const std::string_view list)
{ {
std::vector<stmdsp::dacsample_t> samples; std::vector<stmdsp::dacsample_t> samples;
while (listStr.size() > 0 && samples.size() <= stmdsp::SAMPLES_MAX * 2) { auto it = list.cbegin();
auto numberEnd = listStr.find_first_not_of("0123456789"); while (it != list.cend() && samples.size() < stmdsp::SAMPLES_MAX * 2) {
const auto end = list.find_first_not_of("0123456789",
std::distance(list.cbegin(), it));
const auto itend = end != std::string_view::npos ? list.cbegin() + end
: list.cend();
unsigned long n; unsigned long n;
auto end = numberEnd != std::string_view::npos ? listStr.begin() + numberEnd : listStr.end(); const auto [ptr, ec] = std::from_chars(it, itend, n);
auto [ptr, ec] = std::from_chars(listStr.begin(), end, n);
if (ec != std::errc()) if (ec != std::errc())
break; break;
samples.push_back(n & 4095); samples.push_back(n & 4095);
if (end == listStr.end()) it = itend;
break;
listStr = listStr.substr(numberEnd + 1);
} }
if (samples.size() <= stmdsp::SAMPLES_MAX * 2) { if (samples.size() <= stmdsp::SAMPLES_MAX * 2) {
// DAC buffer must be of even size // DAC buffer must be of even size
if ((samples.size() & 1) == 1) if (samples.size() % 2 != 0)
samples.push_back(samples.back()); samples.push_back(samples.back());
if (m_device) if (m_device)
m_device->siggen_upload(&samples[0], samples.size()); m_device->siggen_upload(samples.data(), samples.size());
log("Generator ready."); log("Generator ready.");
} else { } else {
log("Error: Too many samples for signal generator."); log("Error: Too many samples for signal generator.");
@ -703,9 +705,9 @@ void deviceGenLoadFormula(std::string_view formula)
{ {
auto samples = deviceGenLoadFormulaEval(formula); auto samples = deviceGenLoadFormulaEval(formula);
if (samples.size() > 0) { if (!samples.empty()) {
if (m_device) if (m_device)
m_device->siggen_upload(&samples[0], samples.size()); m_device->siggen_upload(samples.data(), samples.size());
log("Generator ready."); log("Generator ready.");
} else { } else {

@ -17,6 +17,7 @@
#include "stmdsp_code.hpp" #include "stmdsp_code.hpp"
#include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
@ -34,6 +35,7 @@ enum class FileAction {
Save, Save,
SaveAs SaveAs
}; };
static FileAction fileAction = FileAction::None; static FileAction fileAction = FileAction::None;
static std::string fileCurrentPath; static std::string fileCurrentPath;
static std::vector<std::filesystem::path> fileTemplateList; static std::vector<std::filesystem::path> fileTemplateList;
@ -56,17 +58,31 @@ static void openCurrentFile()
} }
} }
void openNewFile() static void openNewFile()
{ {
fileCurrentPath.clear(); fileCurrentPath.clear();
editor.SetText(stmdsp::file_content); editor.SetText(stmdsp::file_content);
} }
void fileScanTemplates() static std::vector<std::filesystem::path> fileScanTemplates()
{
const auto path = std::filesystem::current_path() / "templates";
const std::filesystem::recursive_directory_iterator rdi (path);
std::vector<std::filesystem::path> list;
std::transform(
std::filesystem::begin(rdi),
std::filesystem::end(rdi),
std::back_inserter(list),
[](const auto& file) { return file.path(); });
std::sort(list.begin(), list.end());
return list;
}
void fileInit()
{ {
auto path = std::filesystem::current_path() / "templates"; fileTemplateList = fileScanTemplates();
for (const auto& file : std::filesystem::recursive_directory_iterator{path}) openNewFile();
fileTemplateList.push_back(file.path());
} }
void fileRenderMenu() void fileRenderMenu()

@ -32,8 +32,7 @@ extern void guiRender(void (*func)());
extern void fileRenderMenu(); extern void fileRenderMenu();
extern void fileRenderDialog(); extern void fileRenderDialog();
extern void fileScanTemplates(); extern void fileInit();
extern void openNewFile();
extern void codeEditorInit(); extern void codeEditorInit();
extern void codeRenderMenu(); extern void codeRenderMenu();
@ -60,9 +59,8 @@ int main(int, char **)
if (!guiInitialize()) if (!guiInitialize())
return -1; return -1;
fileScanTemplates();
codeEditorInit(); codeEditorInit();
openNewFile(); fileInit();
while (!done) { while (!done) {
auto endTime = std::chrono::steady_clock::now() + auto endTime = std::chrono::steady_clock::now() +
@ -90,6 +88,7 @@ int main(int, char **)
ImGuiWindowFlags_NoBringToFrontOnFocus); ImGuiWindowFlags_NoBringToFrontOnFocus);
// Render main controls (order is important). // Render main controls (order is important).
{
ImGui::PushFont(fontSans); ImGui::PushFont(fontSans);
codeRenderToolbar(); codeRenderToolbar();
deviceRenderToolbar(); deviceRenderToolbar();
@ -103,8 +102,8 @@ int main(int, char **)
ImGui::SetNextWindowSize({WINDOW_WIDTH, 200}); ImGui::SetNextWindowSize({WINDOW_WIDTH, 200});
logView.Draw("log", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus); logView.Draw("log", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopFont(); ImGui::PopFont();
}
// Finish main view rendering.
ImGui::End(); ImGui::End();
deviceRenderDraw(); deviceRenderDraw();

@ -13,18 +13,23 @@
#include <serial/serial.h> #include <serial/serial.h>
#include <algorithm>
extern void log(const std::string& str); extern void log(const std::string& str);
namespace stmdsp namespace stmdsp
{ {
std::list<std::string>& scanner::scan() const std::forward_list<std::string>& scanner::scan()
{ {
auto devices = serial::list_ports(); auto devices = serial::list_ports();
for (auto& device : devices) { auto foundDevicesEnd = std::remove_if(
if (device.hardware_id.find(STMDSP_USB_ID) != std::string::npos) devices.begin(), devices.end(),
m_available_devices.emplace_front(device.port); [](const auto& dev) {
} return dev.hardware_id.find(STMDSP_USB_ID) == std::string::npos;
});
std::transform(devices.begin(), foundDevicesEnd,
std::front_inserter(m_available_devices),
[](const auto& dev) { return dev.port; });
return m_available_devices; return m_available_devices;
} }
@ -33,6 +38,7 @@ namespace stmdsp
// This could throw! // This could throw!
m_serial.reset(new serial::Serial(file, 8'000'000, serial::Timeout::simpleTimeout(50))); m_serial.reset(new serial::Serial(file, 8'000'000, serial::Timeout::simpleTimeout(50)));
// Test the ID command.
m_serial->flush(); m_serial->flush();
m_serial->write("i"); m_serial->write("i");
auto id = m_serial->read(7); auto id = m_serial->read(7);
@ -66,98 +72,80 @@ namespace stmdsp
m_serial.release(); m_serial.release();
} }
void device::continuous_set_buffer_size(unsigned int size) { bool device::try_command(std::basic_string<uint8_t> cmd) {
if (connected()) { bool success = false;
m_buffer_size = size;
uint8_t request[3] = {
'B',
static_cast<uint8_t>(size),
static_cast<uint8_t>(size >> 8)
};
if (connected()) {
try { try {
m_serial->write(request, 3); std::scoped_lock lock (m_lock);
m_serial->write(cmd.data(), cmd.size());
success = true;
} catch (...) { } catch (...) {
m_serial.release(); m_serial.release();
log("Lost connection!"); log("Lost connection!");
} }
} }
return success;
} }
void device::set_sample_rate(unsigned int id) { bool device::try_read(std::basic_string<uint8_t> cmd, uint8_t *dest, unsigned int dest_size) {
if (connected()) { bool success = false;
uint8_t request[2] = {
'r',
static_cast<uint8_t>(id)
};
if (connected() && dest && dest_size > 0) {
try { try {
m_serial->write(request, 2); std::scoped_lock lock (m_lock);
m_serial->write(cmd.data(), cmd.size());
m_serial->read(dest, dest_size);
success = true;
} catch (...) { } catch (...) {
m_serial.release(); m_serial.release();
log("Lost connection!"); log("Lost connection!");
} }
} }
return success;
} }
unsigned int device::get_sample_rate() { void device::continuous_set_buffer_size(unsigned int size) {
if (connected() && !is_running()) { if (try_command({
uint8_t request[2] = { 'B',
'r', 0xFF static_cast<uint8_t>(size),
}; static_cast<uint8_t>(size >> 8)}))
{
m_buffer_size = size;
}
}
unsigned char result = 0xFF; void device::set_sample_rate(unsigned int id) {
try { try_command({
m_serial->write(request, 2); 'r',
m_serial->read(&result, 1); static_cast<uint8_t>(id)
} catch (...) { });
m_serial.release();
log("Lost connection!");
} }
unsigned int device::get_sample_rate() {
if (!is_running()) {
uint8_t result = 0xFF;
if (try_read({'r', 0xFF}, &result, 1))
m_sample_rate = result; m_sample_rate = result;
} }
return m_sample_rate; return m_sample_rate;
} }
void device::continuous_start() { void device::continuous_start() {
if (connected()) { if (try_command({'R'}))
try {
m_serial->write("R");
m_is_running = true; m_is_running = true;
} catch (...) {
m_serial.release();
log("Lost connection!");
}
}
} }
void device::continuous_start_measure() { void device::continuous_start_measure() {
if (connected()) { if (try_command({'M'}))
try {
m_serial->write("M");
m_is_running = true; m_is_running = true;
} catch (...) {
m_serial.release();
log("Lost connection!");
}
}
} }
uint32_t device::continuous_start_get_measurement() { uint32_t device::continuous_start_get_measurement() {
uint32_t count = 0; uint32_t count = 0;
if (connected()) { try_read({'m'}, reinterpret_cast<uint8_t *>(&count), sizeof(uint32_t));
try {
m_serial->write("m");
m_serial->read(reinterpret_cast<uint8_t *>(&count), sizeof(uint32_t));
} catch (...) {
m_serial.release();
log("Lost connection!");
}
}
return count / 2; return count / 2;
} }
@ -226,18 +214,11 @@ namespace stmdsp
} }
void device::continuous_stop() { void device::continuous_stop() {
if (connected()) { if (try_command({'S'}))
try {
m_serial->write("S");
m_is_running = false; m_is_running = false;
} catch (...) {
m_serial.release();
log("Lost connection!");
}
}
} }
void device::siggen_upload(dacsample_t *buffer, unsigned int size) { bool device::siggen_upload(dacsample_t *buffer, unsigned int size) {
if (connected()) { if (connected()) {
uint8_t request[3] = { uint8_t request[3] = {
'D', 'D',
@ -245,39 +226,41 @@ namespace stmdsp
static_cast<uint8_t>(size >> 8) static_cast<uint8_t>(size >> 8)
}; };
if (!m_is_siggening) {
try { try {
m_serial->write(request, 3); m_serial->write(request, 3);
// TODO different write size if feeding audio?
m_serial->write((uint8_t *)buffer, size * sizeof(dacsample_t)); m_serial->write((uint8_t *)buffer, size * sizeof(dacsample_t));
} catch (...) { } catch (...) {
m_serial.release(); m_serial.release();
log("Lost connection!"); log("Lost connection!");
} }
} } else {
}
void device::siggen_start() {
if (connected()) {
try { try {
m_serial->write("W"); m_serial->write(request, 3);
m_is_siggening = true; if (m_serial->read(1)[0] == 0)
return false;
else
m_serial->write((uint8_t *)buffer, size * sizeof(dacsample_t));
} catch (...) { } catch (...) {
m_serial.release(); m_serial.release();
log("Lost connection!"); log("Lost connection!");
} }
} }
return true;
} else {
return false;
}
}
void device::siggen_start() {
if (try_command({'W'}))
m_is_siggening = true;
} }
void device::siggen_stop() { void device::siggen_stop() {
if (connected()) { if (try_command({'w'}))
try {
m_serial->write("w");
m_is_siggening = false; m_is_siggening = false;
} catch (...) {
m_serial.release();
log("Lost connection!");
}
}
} }
void device::upload_filter(unsigned char *buffer, size_t size) { void device::upload_filter(unsigned char *buffer, size_t size) {
@ -299,33 +282,22 @@ namespace stmdsp
} }
void device::unload_filter() { void device::unload_filter() {
if (connected()) { try_command({'e'});
try {
m_serial->write("e");
} catch (...) {
m_serial.release();
log("Lost connection!");
}
}
} }
std::pair<RunStatus, Error> device::get_status() { std::pair<RunStatus, Error> device::get_status() {
std::pair<RunStatus, Error> ret; std::pair<RunStatus, Error> ret;
if (connected()) { unsigned char buf[2];
try { if (try_read({'I'}, buf, 2)) {
m_serial->write("I"); ret = {
auto result = m_serial->read(2); static_cast<RunStatus>(buf[0]),
ret = {static_cast<RunStatus>(result[0]), static_cast<Error>(buf[1])
static_cast<Error>(result[1])}; };
bool running = ret.first == RunStatus::Running; bool running = ret.first == RunStatus::Running;
if (m_is_running != running) if (m_is_running != running)
m_is_running = running; m_is_running = running;
} catch (...) {
m_serial.release();
log("Lost connection!");
}
} }
return ret; return ret;

@ -15,33 +15,83 @@
#include <serial/serial.h> #include <serial/serial.h>
#include <cstdint> #include <cstdint>
#include <list> #include <forward_list>
#include <memory> #include <memory>
#include <mutex>
#include <string> #include <string>
#include <tuple> #include <tuple>
namespace stmdsp namespace stmdsp
{ {
/**
* The largest possible size of an ADC or DAC sample buffer, as a sample count.
* Maximum byte size would be `SAMPLES_MAX * sizeof(XXXsample_t)`.
*/
constexpr unsigned int SAMPLES_MAX = 4096; constexpr unsigned int SAMPLES_MAX = 4096;
/**
* ADC samples on all platforms are stored as 16-bit unsigned integers.
*/
using adcsample_t = uint16_t;
/**
* DAC samples on all platforms are stored as 16-bit unsigned integers.
*/
using dacsample_t = uint16_t;
/**
* List of all available platforms.
* Note that some platforms in this list may not have complete support.
*/
enum class platform {
Unknown,
H7, /* Some feature support */
L4, /* Complete feature support */
G4 /* Unsupported, but planned */
};
/**
* Run status states, valued to match what the stmdsp firmware reports.
*/
enum class RunStatus : char { enum class RunStatus : char {
Idle = '1', Idle = '1', /* Device ready for commands or execution. */
Running, Running, /* Device currently executing its algorithm. */
Recovering Recovering /* Device recovering from fault caused by algorithm. */
}; };
/**
* Error messages that are reported by the firmware.
*/
enum class Error : char { enum class Error : char {
None = 0, None = 0,
BadParam, BadParam, /* An invalid parameter was passed for a command. */
BadParamSize, BadParamSize, /* An invaild param. size was given for a command. */
BadUserCodeLoad, BadUserCodeLoad, /* Device failed to load the given algorithm. */
BadUserCodeSize, BadUserCodeSize, /* The given algorithm is too large for the device. */
NotIdle, NotIdle, /* An idle-only command was received while not Idle. */
ConversionAborted ConversionAborted /* A conversion was aborted due to a fault. */
}; };
/**
* Provides functionality to scan the system for stmdsp devices.
* A list of devices is returned, though the GUI only interacts with one
* device at a time.
*/
class scanner class scanner
{ {
public:
/**
* Scans for connected devices, returning a list of ports with
* connected stmdsp devices.
*/
const std::forward_list<std::string>& scan();
/**
* Retrieves the results of the last scan().
*/
const std::forward_list<std::string>& devices() const noexcept {
return m_available_devices;
}
private: private:
constexpr static const char *STMDSP_USB_ID = constexpr static const char *STMDSP_USB_ID =
#ifndef STMDSP_WIN32 #ifndef STMDSP_WIN32
@ -50,24 +100,7 @@ namespace stmdsp
"USB\\VID_0483&PID_5740"; "USB\\VID_0483&PID_5740";
#endif #endif
public: std::forward_list<std::string> m_available_devices;
std::list<std::string>& scan();
auto& devices() {
return m_available_devices;
}
private:
std::list<std::string> m_available_devices;
};
using adcsample_t = uint16_t;
using dacsample_t = uint16_t;
enum class platform {
Unknown,
H7, /* Behind in feature support */
L4, /* Complete feature support */
G4 /* Currently unsupported */
}; };
class device class device
@ -96,7 +129,7 @@ namespace stmdsp
std::vector<adcsample_t> continuous_read(); std::vector<adcsample_t> continuous_read();
std::vector<adcsample_t> continuous_read_input(); std::vector<adcsample_t> continuous_read_input();
void siggen_upload(dacsample_t *buffer, unsigned int size); bool siggen_upload(dacsample_t *buffer, unsigned int size);
void siggen_start(); void siggen_start();
void siggen_stop(); void siggen_stop();
@ -116,6 +149,11 @@ namespace stmdsp
unsigned int m_sample_rate = 0; unsigned int m_sample_rate = 0;
bool m_is_siggening = false; bool m_is_siggening = false;
bool m_is_running = false; bool m_is_running = false;
std::mutex m_lock;
bool try_command(std::basic_string<uint8_t> data);
bool try_read(std::basic_string<uint8_t> cmd, uint8_t *dest, unsigned int dest_size);
}; };
} }

@ -4,6 +4,7 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>
#include <vector>
namespace wav namespace wav
{ {
@ -64,31 +65,30 @@ namespace wav
file.read(reinterpret_cast<char *>(&d), sizeof(wav::data)); file.read(reinterpret_cast<char *>(&d), sizeof(wav::data));
if (!d.valid()) if (!d.valid())
return; return;
m_data = new char[d.size + 4096 - (d.size % 4096)]; m_data.resize(d.size / sizeof(int16_t));
m_size = d.size; m_next = m_data.begin();
file.read(m_data, d.size); file.read(reinterpret_cast<char *>(m_data.data()), d.size);
} }
} }
clip() = default; clip() = default;
bool valid() const { bool valid() const {
return m_data != nullptr && m_size > 0; return !m_data.empty();
} }
auto data() const { const int16_t *data() const {
return m_data; return m_data.data();
} }
auto next(unsigned int chunksize = 3000) { void next(int16_t *buf, unsigned int size) {
if (m_pos == m_size) { for (unsigned int i = 0; i < size; ++i) {
m_pos = 0; if (m_next == m_data.end())
m_next = m_data.begin();
else
*buf++ = *m_next++;
} }
auto ret = m_data + m_pos;
m_pos = std::min(m_pos + chunksize, m_size);
return ret;
} }
private: private:
char *m_data = nullptr; std::vector<int16_t> m_data;
uint32_t m_size = 0; decltype(m_data.begin()) m_next;
uint32_t m_pos = 0;
}; };
} }

Loading…
Cancel
Save