]> code.bitgloo.com Git - clyne/stmdspgui.git/commitdiff
code cleanup; fix audio streaming
authorClyne Sullivan <clyne@bitgloo.com>
Sat, 20 Nov 2021 21:55:26 +0000 (16:55 -0500)
committerClyne Sullivan <clyne@bitgloo.com>
Sat, 20 Nov 2021 21:55:26 +0000 (16:55 -0500)
source/code.cpp
source/device.cpp
source/file.cpp
source/main.cpp
source/stmdsp/stmdsp.cpp
source/stmdsp/stmdsp.hpp
source/wav.hpp

index e29044013aec6966f223a3e63df1bae7015e4044..163d6e5397c7554f9ab3d386f96a24944911f500 100644 (file)
@@ -33,6 +33,8 @@ std::string tempFileName; // device.cpp
 static std::string editorCompiled;
 
 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 disassembleCode();
 
@@ -64,6 +66,39 @@ void codeRenderWidgets()
     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()
 {
     log("Compiling...");
@@ -74,35 +109,28 @@ void compileEditorCode()
         std::filesystem::remove(tempFileName + ".orig.o");
     }
 
-    stmdsp::platform platform;
-    if (m_device) {
-        platform = m_device->get_platform();
-    } else {
-        // Assume a default.
-        platform = stmdsp::platform::L4;
-    }
+    const auto platform = m_device ? m_device->get_platform()
+                                   : stmdsp::platform::L4;
 
-    if (tempFileName.size() == 0)
+    if (tempFileName.empty())
         tempFileName = newTempFileName();
 
+
     {
         std::ofstream file (tempFileName, std::ios::trunc | std::ios::binary);
 
-        auto file_text = platform == stmdsp::platform::L4 ? stmdsp::file_header_l4
-                                                          : stmdsp::file_header_h7;
-        auto samples_text = std::to_string(m_device ? m_device->get_buffer_size()
-                                                    : 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;
-        }
+        auto file_text =
+            platform == stmdsp::platform::L4 ? stmdsp::file_header_l4
+                                             : stmdsp::file_header_h7;
+        const auto buffer_size = m_device ? m_device->get_buffer_size()
+                                          : stmdsp::SAMPLES_MAX;
+
+        stringReplaceAll(file_text, "$0", std::to_string(buffer_size));
 
-        file << file_text;
-        file << "\n";
-        file << editor.GetText();
+        file << file_text << '\n' << editor.GetText();
     }
 
-    constexpr const char *script_ext =
+    const auto scriptFile = tempFileName +
 #ifndef STMDSP_WIN32
         ".sh";
 #else
@@ -110,44 +138,33 @@ void compileEditorCode()
 #endif
 
     {
-        std::ofstream makefile (tempFileName + script_ext, std::ios::binary);
-        auto make_text = platform == stmdsp::platform::L4 ? stmdsp::makefile_text_l4
-                                                          : 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;) {
-            make_text.replace(i, 2, tempFileName);
-            i += 2;
-        }
-        for (std::size_t i = 0; (i = make_text.find("$1", i)) != std::string::npos;) {
-            make_text.replace(i, 2, cwd);
-            i += 2;
-        }
+        std::ofstream makefile (scriptFile, std::ios::binary);
+        auto make_text =
+            platform == stmdsp::platform::L4 ? stmdsp::makefile_text_l4
+                                             : stmdsp::makefile_text_h7;
+
+        stringReplaceAll(make_text, "$0", tempFileName);
+        stringReplaceAll(make_text, "$1",
+                         std::filesystem::current_path().string());
 
         makefile << make_text;
     }
 
-    auto makeOutput = tempFileName + script_ext + ".log";
-    auto makeCommand = tempFileName + script_ext + " > " + makeOutput + " 2>&1";
-
 #ifndef STMDSP_WIN32
-    system((std::string("chmod +x ") + tempFileName + script_ext).c_str());
+    system((std::string("chmod +x ") + scriptFile).c_str());
 #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();
         log("Compilation succeeded.");
     } else {
         log("Compilation failed.");
     }
+
+    std::filesystem::remove(tempFileName);
+    std::filesystem::remove(scriptFile);
 }
 
 void disassembleCode()
@@ -158,31 +175,15 @@ void disassembleCode()
         compileEditorCode();
     }
 
-    auto output = tempFileName + ".asm.log";
-    auto command = std::string("arm-none-eabi-objdump -d --no-show-raw-insn ") +
+    const auto output = tempFileName + ".asm.log";
+    const auto command =
+        std::string("arm-none-eabi-objdump -d --no-show-raw-insn ") +
         tempFileName + ".orig.o > " + output + " 2>&1";
-    
-    if (system(command.c_str()) == 0) {
-        {
-            std::ifstream result_file (output);
-            std::ostringstream sstr;
-            sstr << result_file.rdbuf();
-            log(sstr.str().c_str());
-        }
-
-        ImGui::OpenPopup("compile");
-        std::filesystem::remove(output);
 
+    if (codeExecuteCommand(command, output)) {
         log("Ready.");
     } else {
         log("Failed to load disassembly.");
     }
 }
 
-std::string newTempFileName()
-{
-    auto tempPath = std::filesystem::temp_directory_path();
-    tempPath /= "stmdspgui_build";
-    return tempPath.string();
-}
-
index 399bf7d7a998657a1577ebb4ea3556098e369aa3..39bc746254330046ea622fdd310c494e26d9a7fb 100644 (file)
@@ -9,11 +9,6 @@
  * If not, see <https://www.gnu.org/licenses/>.
  */
 
-/**
- * TODO list:
- * - Improve signal generator audio streaming.
- */
-
 #include "stmdsp.hpp"
 
 #include "imgui.h"
@@ -80,11 +75,41 @@ static unsigned int drawSamplesBufferSize = 1;
 
 static void measureCodeTask(std::shared_ptr<stmdsp::device> device)
 {
-    if (!device)
-        return;
-    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
-    auto cycles = device->continuous_start_get_measurement();
-    log(std::string("Execution time: ") + std::to_string(cycles) + " cycles.");
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+
+    if (device) {
+        const auto cycles = device->continuous_start_get_measurement();
+        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)
@@ -93,71 +118,45 @@ static void drawSamplesTask(std::shared_ptr<stmdsp::device> device)
         return;
 
     const bool doLogger = logResults && logSamplesFile.good();
-
-    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;
+    const auto bufferTime = getBufferPeriod(device);
 
     std::unique_lock<std::timed_mutex> lockDraw (mutexDrawSamples, std::defer_lock);
     std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
 
-    while (m_device && m_device->is_running()) {
-        auto next = std::chrono::high_resolution_clock::now() +
-                    std::chrono::microseconds(bufferTime);
+    auto addToQueue = [&lockDraw](auto& queue, const auto& chunk) {
+        lockDraw.lock();
+        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)) {
-            chunk = m_device->continuous_read();
-            int tries = -1;
-            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();
-            }
+            const auto chunk = tryReceiveChunk(device,
+                std::mem_fn(&stmdsp::device::continuous_read));
             lockDevice.unlock();
+
+            addToQueue(drawSamplesQueue, chunk);
+            if (doLogger) {
+                for (const auto& s : chunk)
+                    logSamplesFile << s << '\n';
+            }
         } else {
-            // Cooldown.
+            // Device must be busy, cooldown.
             std::this_thread::sleep_for(std::chrono::milliseconds(500));
         }
 
         if (drawSamplesInput && popupRequestDraw) {
-            std::vector<stmdsp::dacsample_t> chunk2;
-
             if (lockDevice.try_lock_for(std::chrono::milliseconds(1))) {
-                chunk2 = m_device->continuous_read_input();
-                int tries = -1;
-                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();
-                }
+                const auto chunk2 = tryReceiveChunk(device,
+                    std::mem_fn(&stmdsp::device::continuous_read_input));
                 lockDevice.unlock();
-            }
 
-            lockDraw.lock();
-            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';
+                addToQueue(drawSamplesInputQueue, chunk2);
             }
-            lockDraw.unlock();
         }
-        
+
         std::this_thread::sleep_until(next);
     }
 }
@@ -167,36 +166,36 @@ static void feedSigGenTask(std::shared_ptr<stmdsp::device> device)
     if (!device)
         return;
 
-    const auto bufferSize = m_device->get_buffer_size();
-    const double sampleRate = sampleRateInts[m_device->get_sample_rate()];
-    const unsigned long delay = bufferSize / sampleRate * 0.975 * 1e6;
+    const auto delay = getBufferPeriod(device);
+    const auto uploadDelay = getBufferPeriod(device, 0.001);
 
-    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);
 
     lockDevice.lock();
-    // One (or both) of these freezes the device...
-    m_device->siggen_upload(wavBuf.data(), wavBuf.size());
-    //m_device->siggen_start();
+    device->siggen_upload(wavBuf.data(), wavBuf.size());
+    wavBuf.resize(wavBuf.size() / 2);
+    device->siggen_start();
+    std::this_thread::sleep_for(std::chrono::milliseconds(1));
     lockDevice.unlock();
 
-    std::this_thread::sleep_for(std::chrono::microseconds(delay));
+    std::vector<int16_t> wavIntBuf (wavBuf.size());
 
-    return;
     while (genRunning) {
-        auto next = std::chrono::high_resolution_clock::now() +
-                    std::chrono::microseconds(delay);
+        const auto next = std::chrono::high_resolution_clock::now() + delay;
 
-        auto src = reinterpret_cast<uint16_t *>(wavOutput.next(bufferSize));
-        for (auto& w : wavBuf)
-            w = *src++ / 16 + 2048;
+        wavOutput.next(wavIntBuf.data(), wavIntBuf.size());
+        auto src = wavIntBuf.cbegin();
+        std::generate(wavBuf.begin(), wavBuf.end(),
+            [&src] { return static_cast<stmdsp::dacsample_t>(*src++ / 16 + 2048); });
 
-        if (lockDevice.try_lock_until(next)) {
-            m_device->siggen_upload(wavBuf.data(), wavBuf.size());
-            lockDevice.unlock();
-            std::this_thread::sleep_until(next);
-        }
+        lockDevice.lock();
+        while (!device->siggen_upload(wavBuf.data(), wavBuf.size()))
+            std::this_thread::sleep_for(uploadDelay);
+        lockDevice.unlock();
+
+        std::this_thread::sleep_until(next);
     }
 }
 
@@ -208,16 +207,20 @@ static void statusTask(std::shared_ptr<stmdsp::device> device)
     while (device->connected()) {
         std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
         lockDevice.lock();
-        auto [status, error] = device->get_status();
+        const auto [status, error] = device->get_status();
         lockDevice.unlock();
 
         if (error != stmdsp::Error::None) {
-            if (error == stmdsp::Error::NotIdle) {
+            switch (error) {
+            case stmdsp::Error::NotIdle:
                 log("Error: Device already running...");
-            } else if (error == stmdsp::Error::ConversionAborted) {
+                break;
+            case stmdsp::Error::ConversionAborted:
                 log("Error: Algorithm unloaded, a fault occurred!");
-            } else {
+                break;
+            default:
                 log("Error: Device had an issue...");
+                break;
             }
         }
 
@@ -383,13 +386,13 @@ void deviceRenderDraw()
             drawSamplesBufferSize = std::round(sr * tf);
         }
         ImGui::SameLine();
-        ImGui::Text("Y-minmax: %u", yMinMax);
+        ImGui::Text("Y: +/-%1.2fV", 3.3f * (static_cast<float>(yMinMax) / 4095.f));
         ImGui::SameLine();
-        if (ImGui::Button("--", {30, 0})) {
+        if (ImGui::Button(" - ", {30, 0})) {
             yMinMax = std::max(63u, yMinMax >> 1);
         }
         ImGui::SameLine();
-        if (ImGui::Button("++", {30, 0})) {
+        if (ImGui::Button(" + ", {30, 0})) {
             yMinMax = std::min(4095u, (yMinMax << 1) | 1);
         }
 
@@ -508,7 +511,7 @@ void deviceRenderMenu()
             popupRequestBuffer = true;
         }
         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;
         }
         static const char *startSiggenLabel = "Start signal generator";
@@ -542,22 +545,26 @@ void deviceRenderToolbar()
         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)) {
         for (unsigned int i = 0; i < sampleRateList.size(); ++i) {
             if (ImGui::Selectable(sampleRateList[i])) {
                 sampleRatePreview = sampleRateList[i];
-                if (m_device && !m_device->is_running()) {
-                    do {
-                        m_device->set_sample_rate(i);
-                        std::this_thread::sleep_for(std::chrono::milliseconds(10));
-                    } while (m_device->get_sample_rate() != i);
+                do {
+                    m_device->set_sample_rate(i);
+                    std::this_thread::sleep_for(std::chrono::milliseconds(10));
+                } while (m_device->get_sample_rate() != i);
 
-                    drawSamplesBufferSize = std::round(sampleRateInts[i] * drawSamplesTimeframe);
-                }
+                drawSamplesBufferSize = std::round(sampleRateInts[i] * drawSamplesTimeframe);
             }
         }
         ImGui::EndCombo();
     }
+    if (!enable)
+        ImGui::PopDisabled();
 }
 
 void deviceConnect()
@@ -566,7 +573,7 @@ void deviceConnect()
 
     if (!m_device) {
         stmdsp::scanner scanner;
-        if (auto devices = scanner.scan(); devices.size() > 0) {
+        if (auto devices = scanner.scan(); !devices.empty()) {
             try {
                 m_device.reset(new stmdsp::device(devices.front()));
             } catch (...) {
@@ -608,8 +615,7 @@ void deviceStart()
 
     if (m_device->is_running()) {
         {
-            std::scoped_lock lock (mutexDrawSamples);
-            std::scoped_lock lock2 (mutexDeviceLoad);
+            std::scoped_lock lock (mutexDrawSamples, mutexDeviceLoad);
             std::this_thread::sleep_for(std::chrono::microseconds(150));
             m_device->continuous_stop();
         }
@@ -637,10 +643,9 @@ void deviceAlgorithmUpload()
     if (!m_device) {
         log("No device connected.");
         return;
-    }
-
-    if (m_device->is_running())
+    } else if (m_device->is_running()) {
         return;
+    }
 
     if (std::ifstream algo (tempFileName + ".o"); algo.good()) {
         std::ostringstream sstr;
@@ -658,41 +663,38 @@ void deviceAlgorithmUnload()
 {
     if (!m_device) {
         log("No device connected.");
-        return;
-    }
-
-    if (!m_device->is_running()) {
+    } else if (!m_device->is_running()) {
         m_device->unload_filter();
         log("Algorithm unloaded.");
     }
 }
 
-void deviceGenLoadList(std::string_view listStr)
+void deviceGenLoadList(const std::string_view list)
 {
     std::vector<stmdsp::dacsample_t> samples;
 
-    while (listStr.size() > 0 && samples.size() <= stmdsp::SAMPLES_MAX * 2) {
-        auto numberEnd = listStr.find_first_not_of("0123456789");
-
+    auto it = list.cbegin();
+    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;
-        auto end = numberEnd != std::string_view::npos ? listStr.begin() + numberEnd : listStr.end();
-        auto [ptr, ec] = std::from_chars(listStr.begin(), end, n);
+        const auto [ptr, ec] = std::from_chars(it, itend, n);
         if (ec != std::errc())
             break;
 
         samples.push_back(n & 4095);
-        if (end == listStr.end())
-            break;
-        listStr = listStr.substr(numberEnd + 1);
+        it = itend;
     }
 
     if (samples.size() <= stmdsp::SAMPLES_MAX * 2) {
         // DAC buffer must be of even size
-        if ((samples.size() & 1) == 1)
+        if (samples.size() % 2 != 0)
             samples.push_back(samples.back());
 
         if (m_device)
-            m_device->siggen_upload(&samples[0], samples.size());
+            m_device->siggen_upload(samples.data(), samples.size());
         log("Generator ready.");
     } else {
         log("Error: Too many samples for signal generator.");
@@ -703,9 +705,9 @@ void deviceGenLoadFormula(std::string_view formula)
 {
     auto samples = deviceGenLoadFormulaEval(formula);
 
-    if (samples.size() > 0) {
+    if (!samples.empty()) {
         if (m_device)
-            m_device->siggen_upload(&samples[0], samples.size());
+            m_device->siggen_upload(samples.data(), samples.size());
 
         log("Generator ready.");
     } else {
index 0f0015c63c7974b06d2cbc8ee8132981123b8c6c..dfd914827502c588f5b82a02ed43ead6465c15c8 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "stmdsp_code.hpp"
 
+#include <algorithm>
 #include <cstdlib>
 #include <filesystem>
 #include <fstream>
@@ -34,6 +35,7 @@ enum class FileAction {
     Save,
     SaveAs
 };
+
 static FileAction fileAction = FileAction::None;
 static std::string fileCurrentPath;
 static std::vector<std::filesystem::path> fileTemplateList;
@@ -56,17 +58,31 @@ static void openCurrentFile()
     }
 }
 
-void openNewFile()
+static void openNewFile()
 {
     fileCurrentPath.clear();
     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";
-    for (const auto& file : std::filesystem::recursive_directory_iterator{path})
-        fileTemplateList.push_back(file.path());
+    fileTemplateList = fileScanTemplates();
+    openNewFile();
 }
 
 void fileRenderMenu()
index b98ec2b0c1649f6c9b346db0b45c60492d63d328..20c8ea1e60b3ebccb136643837ecb98887ef286b 100644 (file)
@@ -32,8 +32,7 @@ extern void guiRender(void (*func)());
 
 extern void fileRenderMenu();
 extern void fileRenderDialog();
-extern void fileScanTemplates();
-extern void openNewFile();
+extern void fileInit();
 
 extern void codeEditorInit();
 extern void codeRenderMenu();
@@ -60,9 +59,8 @@ int main(int, char **)
     if (!guiInitialize())
         return -1;
 
-    fileScanTemplates();
     codeEditorInit();
-    openNewFile();
+    fileInit();
 
     while (!done) {
         auto endTime = std::chrono::steady_clock::now() +
@@ -90,21 +88,22 @@ int main(int, char **)
                          ImGuiWindowFlags_NoBringToFrontOnFocus);
 
         // Render main controls (order is important).
-        ImGui::PushFont(fontSans);
-        codeRenderToolbar();
-        deviceRenderToolbar();
-        fileRenderDialog();
-        deviceRenderWidgets();
-        ImGui::PopFont();
-
-        ImGui::PushFont(fontMono);
-        codeRenderWidgets();
-        ImGui::SetNextWindowPos({0, WINDOW_HEIGHT - 200});
-        ImGui::SetNextWindowSize({WINDOW_WIDTH, 200});
-        logView.Draw("log", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
-        ImGui::PopFont();
-
-        // Finish main view rendering.
+        {
+            ImGui::PushFont(fontSans);
+            codeRenderToolbar();
+            deviceRenderToolbar();
+            fileRenderDialog();
+            deviceRenderWidgets();
+            ImGui::PopFont();
+
+            ImGui::PushFont(fontMono);
+            codeRenderWidgets();
+            ImGui::SetNextWindowPos({0, WINDOW_HEIGHT - 200});
+            ImGui::SetNextWindowSize({WINDOW_WIDTH, 200});
+            logView.Draw("log", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
+            ImGui::PopFont();
+        }
+
         ImGui::End();
 
         deviceRenderDraw();
index 650dbc41f78e621d719989406ffb1b57bbbae35a..2bbb92b8ed4d1ae6b059fedbac4689fcd1a2c5df 100644 (file)
 
 #include <serial/serial.h>
 
+#include <algorithm>
+
 extern void log(const std::string& str);
 
 namespace stmdsp
 {
-    std::list<std::string>& scanner::scan()
+    const std::forward_list<std::string>& scanner::scan()
     {
         auto devices = serial::list_ports();
-        for (auto& device : devices) {
-            if (device.hardware_id.find(STMDSP_USB_ID) != std::string::npos)
-                m_available_devices.emplace_front(device.port);
-        }
-
+        auto foundDevicesEnd = std::remove_if(
+            devices.begin(), devices.end(),
+            [](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;
     }
 
@@ -33,6 +38,7 @@ namespace stmdsp
         // This could throw!
         m_serial.reset(new serial::Serial(file, 8'000'000, serial::Timeout::simpleTimeout(50)));
 
+        // Test the ID command.
         m_serial->flush();
         m_serial->write("i");
         auto id = m_serial->read(7);
@@ -66,98 +72,80 @@ namespace stmdsp
             m_serial.release();
     }
 
-    void device::continuous_set_buffer_size(unsigned int size) {
-        if (connected()) {
-            m_buffer_size = size;
-
-            uint8_t request[3] = {
-                'B',
-                static_cast<uint8_t>(size),
-                static_cast<uint8_t>(size >> 8)
-            };
+    bool device::try_command(std::basic_string<uint8_t> cmd) {
+        bool success = false;
 
+        if (connected()) {
             try {
-                m_serial->write(request, 3);
+                std::scoped_lock lock (m_lock);
+                m_serial->write(cmd.data(), cmd.size());
+                success = true;
             } catch (...) {
                 m_serial.release();
                 log("Lost connection!");
             }
         }
+
+        return success;
     }
 
-    void device::set_sample_rate(unsigned int id) {
-        if (connected()) {
-            uint8_t request[2] = {
-                'r',
-                static_cast<uint8_t>(id)
-            };
+    bool device::try_read(std::basic_string<uint8_t> cmd, uint8_t *dest, unsigned int dest_size) {
+        bool success = false;
 
+        if (connected() && dest && dest_size > 0) {
             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 (...) {
                 m_serial.release();
                 log("Lost connection!");
             }
         }
+
+        return success;
     }
 
-    unsigned int device::get_sample_rate() {
-        if (connected() && !is_running()) {
-            uint8_t request[2] = {
-                'r', 0xFF
-            };
+    void device::continuous_set_buffer_size(unsigned int size) {
+        if (try_command({
+                'B',
+                static_cast<uint8_t>(size),
+                static_cast<uint8_t>(size >> 8)}))
+        {
+            m_buffer_size = size;
+        }
+    }
 
-            unsigned char result = 0xFF;
-            try {
-                m_serial->write(request, 2);
-                m_serial->read(&result, 1);
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
+    void device::set_sample_rate(unsigned int id) {
+        try_command({
+            'r',
+            static_cast<uint8_t>(id)
+        });
+    }
 
-            m_sample_rate = result;
+    unsigned int device::get_sample_rate() {
+        if (!is_running()) {
+            uint8_t result = 0xFF;
+            if (try_read({'r', 0xFF}, &result, 1))
+                m_sample_rate = result;
         }
-
         return m_sample_rate;
     }
 
     void device::continuous_start() {
-        if (connected()) {
-            try {
-                m_serial->write("R");
-                m_is_running = true;
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
-        }
+        if (try_command({'R'}))
+            m_is_running = true;
     }
 
     void device::continuous_start_measure() {
-        if (connected()) {
-            try {
-                m_serial->write("M");
-                m_is_running = true;
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
-        }
+        if (try_command({'M'}))
+            m_is_running = true;
     }
 
     uint32_t device::continuous_start_get_measurement() {
         uint32_t count = 0;
-        if (connected()) {
-            try {
-                m_serial->write("m");
-                m_serial->read(reinterpret_cast<uint8_t *>(&count), sizeof(uint32_t));
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
-        }
-
+        try_read({'m'}, reinterpret_cast<uint8_t *>(&count), sizeof(uint32_t));
         return count / 2;
     }
 
@@ -226,18 +214,11 @@ namespace stmdsp
     }
 
     void device::continuous_stop() {
-        if (connected()) {
-            try {
-                m_serial->write("S");
-                m_is_running = false;
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
-        }
+        if (try_command({'S'}))
+            m_is_running = false;
     }
 
-    void device::siggen_upload(dacsample_t *buffer, unsigned int size) {
+    bool device::siggen_upload(dacsample_t *buffer, unsigned int size) {
         if (connected()) {
             uint8_t request[3] = {
                 'D',
@@ -245,39 +226,41 @@ namespace stmdsp
                 static_cast<uint8_t>(size >> 8)
             };
 
-            try {
-                m_serial->write(request, 3);
-                // TODO different write size if feeding audio?
-                m_serial->write((uint8_t *)buffer, size * sizeof(dacsample_t));
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
+            if (!m_is_siggening) {
+                try {
+                    m_serial->write(request, 3);
+                    m_serial->write((uint8_t *)buffer, size * sizeof(dacsample_t));
+                } catch (...) {
+                    m_serial.release();
+                    log("Lost connection!");
+                }
+            } else {
+                try {
+                    m_serial->write(request, 3);
+                    if (m_serial->read(1)[0] == 0)
+                        return false;
+                    else
+                        m_serial->write((uint8_t *)buffer, size * sizeof(dacsample_t));
+                } catch (...) {
+                    m_serial.release();
+                    log("Lost connection!");
+                }
             }
+
+            return true;
+        } else {
+            return false;
         }
     }
 
     void device::siggen_start() {
-        if (connected()) {
-            try {
-                m_serial->write("W");
-                m_is_siggening = true;
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
-        }
+        if (try_command({'W'}))
+            m_is_siggening = true;
     }
 
     void device::siggen_stop() {
-        if (connected()) {
-            try {
-                m_serial->write("w");
-                m_is_siggening = false;
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
-        }
+        if (try_command({'w'}))
+            m_is_siggening = false;
     }
 
     void device::upload_filter(unsigned char *buffer, size_t size) {
@@ -299,33 +282,22 @@ namespace stmdsp
     }
 
     void device::unload_filter() {
-        if (connected()) {
-            try {
-                m_serial->write("e");
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
-        }
+        try_command({'e'});
     }
 
     std::pair<RunStatus, Error> device::get_status() {
         std::pair<RunStatus, Error> ret;
 
-        if (connected()) {
-            try {
-                m_serial->write("I");
-                auto result = m_serial->read(2);
-                ret = {static_cast<RunStatus>(result[0]),
-                       static_cast<Error>(result[1])};
-
-                bool running = ret.first == RunStatus::Running;
-                if (m_is_running != running)
-                    m_is_running = running;
-            } catch (...) {
-                m_serial.release();
-                log("Lost connection!");
-            }
+        unsigned char buf[2];
+        if (try_read({'I'}, buf, 2)) {
+            ret = {
+                static_cast<RunStatus>(buf[0]),
+                static_cast<Error>(buf[1])
+            };
+
+            bool running = ret.first == RunStatus::Running;
+            if (m_is_running != running)
+                m_is_running = running;
         }
 
         return ret;
index 76ca94e09b08653d4947425e2e4d3242cfdf9166..64f5aff6f7eafc5ad0be7da57d55c718c817b6ce 100644 (file)
 #include <serial/serial.h>
 
 #include <cstdint>
-#include <list>
+#include <forward_list>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <tuple>
 
 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;
 
+    /**
+     * 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 {
-        Idle = '1',
-        Running,
-        Recovering
+        Idle = '1', /* Device ready for commands or execution. */
+        Running,    /* Device currently executing its algorithm. */
+        Recovering  /* Device recovering from fault caused by algorithm. */
     };
 
+    /**
+     * Error messages that are reported by the firmware.
+     */
     enum class Error : char {
         None = 0,
-        BadParam,
-        BadParamSize,
-        BadUserCodeLoad,
-        BadUserCodeSize,
-        NotIdle,
-        ConversionAborted
+        BadParam,         /* An invalid parameter was passed for a command. */
+        BadParamSize,     /* An invaild param. size was given for a command. */
+        BadUserCodeLoad,  /* Device failed to load the given algorithm. */
+        BadUserCodeSize,  /* The given algorithm is too large for the device. */
+        NotIdle,          /* An idle-only command was received while not Idle. */
+        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
     {
+    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:
         constexpr static const char *STMDSP_USB_ID =
 #ifndef STMDSP_WIN32
@@ -50,24 +100,7 @@ namespace stmdsp
             "USB\\VID_0483&PID_5740";
 #endif
 
-    public:
-        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 */
+        std::forward_list<std::string> m_available_devices;
     };
 
     class device
@@ -96,7 +129,7 @@ namespace stmdsp
         std::vector<adcsample_t> continuous_read();
         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_stop();
 
@@ -116,6 +149,11 @@ namespace stmdsp
         unsigned int m_sample_rate = 0;
         bool m_is_siggening = 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);
     };
 }
 
index 9ff06e5f36856979493bd4ed7b69a0cbcc72c107..71842bd70b14bacd9e4d8112c9686cb67d509710 100644 (file)
@@ -4,6 +4,7 @@
 #include <cstdint>
 #include <cstring>
 #include <fstream>
+#include <vector>
 
 namespace wav
 {
@@ -64,31 +65,30 @@ namespace wav
                 file.read(reinterpret_cast<char *>(&d), sizeof(wav::data));
                 if (!d.valid())
                     return;
-                m_data = new char[d.size + 4096 - (d.size % 4096)];
-                m_size = d.size;
-                file.read(m_data, d.size);
+                m_data.resize(d.size / sizeof(int16_t));
+                m_next = m_data.begin();
+                file.read(reinterpret_cast<char *>(m_data.data()), d.size);
             }
         }
         clip() = default;
 
         bool valid() const {
-            return m_data != nullptr && m_size > 0;
+            return !m_data.empty();
         }
-        auto data() const {
-            return m_data;
+        const int16_t *data() const {
+            return m_data.data();
         }
-        auto next(unsigned int chunksize = 3000) {
-            if (m_pos == m_size) {
-                m_pos = 0;
+        void next(int16_t *buf, unsigned int size) {
+            for (unsigned int i = 0; i < size; ++i) {
+                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:
-        char *m_data = nullptr;
-        uint32_t m_size = 0;
-        uint32_t m_pos = 0;
+        std::vector<int16_t> m_data;
+        decltype(m_data.begin()) m_next;
     };
 }