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 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();
}

@ -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 {

@ -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()

@ -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();

@ -13,18 +13,23 @@
#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;

@ -15,33 +15,83 @@
#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);
};
}

@ -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;
};
}

Loading…
Cancel
Save