diff options
Diffstat (limited to 'source/device.cpp')
-rw-r--r-- | source/device.cpp | 699 |
1 files changed, 324 insertions, 375 deletions
diff --git a/source/device.cpp b/source/device.cpp index 196f5b4..9c50a0d 100644 --- a/source/device.cpp +++ b/source/device.cpp @@ -11,455 +11,353 @@ #include "stmdsp.hpp" +#include "circular.hpp" #include "imgui.h" -#include "ImGuiFileDialog.h" #include "wav.hpp" +#include <array> +#include <cctype> #include <charconv> +#include <cmath> +#include <deque> #include <fstream> +#include <functional> +#include <iostream> +#include <memory> #include <mutex> +#include <string> +#include <string_view> #include <thread> +#include <vector> -extern std::string tempFileName; -extern stmdsp::device *m_device; extern void log(const std::string& str); +extern std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string&); +extern std::ifstream compileOpenBinaryFile(); +extern void deviceRenderDisconnect(); -extern std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string_view); - -static const char *sampleRateList[6] = { - "8 kHz", - "16 kHz", - "20 kHz", - "32 kHz", - "48 kHz", - "96 kHz" -}; -static const char *sampleRatePreview = sampleRateList[0]; -static const unsigned int sampleRateInts[6] = { - 8'000, - 16'000, - 20'000, - 32'000, - 48'000, - 96'000 -}; - -static bool measureCodeTime = false; -static bool drawSamples = false; -static bool logResults = false; -static bool genRunning = false; -static bool drawSamplesInput = false; - -static bool popupRequestBuffer = false; -static bool popupRequestSiggen = false; -static bool popupRequestDraw = false; -static bool popupRequestLog = false; +std::shared_ptr<stmdsp::device> m_device; -static std::mutex mutexDrawSamples; -static std::vector<stmdsp::dacsample_t> drawSamplesBuf; -static std::vector<stmdsp::dacsample_t> drawSamplesBuf2; +static std::timed_mutex mutexDrawSamples; +static std::timed_mutex mutexDeviceLoad; static std::ofstream logSamplesFile; static wav::clip wavOutput; +static std::deque<stmdsp::dacsample_t> drawSamplesQueue; +static std::deque<stmdsp::dacsample_t> drawSamplesInputQueue; +static bool drawSamplesInput = false; +static unsigned int drawSamplesBufferSize = 1; -static void measureCodeTask(stmdsp::device *device) +bool deviceConnect(); + +void deviceSetInputDrawing(bool enabled) { - if (device == nullptr) - 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."); + drawSamplesInput = enabled; + if (enabled) { + drawSamplesQueue.clear(); + drawSamplesInputQueue.clear(); + } } -static void drawSamplesTask(stmdsp::device *device) +static void measureCodeTask(std::shared_ptr<stmdsp::device> device) { - if (device == nullptr) - return; + std::this_thread::sleep_for(std::chrono::seconds(1)); - const bool doLogger = logResults && logSamplesFile.good(); + if (device) { + const auto cycles = device->measurement_read(); + log(std::string("Execution time: ") + std::to_string(cycles) + " cycles."); + } +} - const auto bsize = m_device->get_buffer_size(); - const float srate = sampleRateInts[m_device->get_sample_rate()]; - const unsigned int delay = bsize / srate * 1000.f * 0.5f; +static std::vector<stmdsp::dacsample_t> tryReceiveChunk( + std::shared_ptr<stmdsp::device> device, + auto readFunc) +{ + for (int tries = 0; tries < 100; ++tries) { + if (!device->is_running()) + break; - while (m_device->is_running()) { - { - std::scoped_lock lock (mutexDrawSamples); - drawSamplesBuf = m_device->continuous_read(); - if (drawSamplesInput && popupRequestDraw) - drawSamplesBuf2 = m_device->continuous_read_input(); - } + const auto chunk = readFunc(device.get()); + if (!chunk.empty()) + return chunk; + else + std::this_thread::sleep_for(std::chrono::microseconds(20)); + } - if (doLogger) { - for (const auto& s : drawSamplesBuf) - logSamplesFile << s << '\n'; - } + return {}; +} - std::this_thread::sleep_for(std::chrono::milliseconds(delay)); +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 = device->get_sample_rate(); + return std::chrono::duration<double>(bufferSize / sampleRate * factor); + } else { + return {}; } - - std::fill(drawSamplesBuf.begin(), drawSamplesBuf.end(), 2048); - std::fill(drawSamplesBuf2.begin(), drawSamplesBuf2.end(), 2048); } -static void feedSigGenTask(stmdsp::device *device) +static void drawSamplesTask(std::shared_ptr<stmdsp::device> device) { - if (device == nullptr) + if (!device) return; - const auto bsize = m_device->get_buffer_size(); - const float srate = sampleRateInts[m_device->get_sample_rate()]; - const unsigned int delay = bsize / srate * 1000.f * 0.4f; + // This is the amount of time to wait between device reads. + const auto bufferTime = getBufferPeriod(device, 1); - auto wavBuf = new stmdsp::adcsample_t[bsize]; + // Adds the given chunk of samples to the given queue. + const auto addToQueue = [](auto& queue, const auto& chunk) { + std::scoped_lock lock (mutexDrawSamples); + std::copy(chunk.cbegin(), chunk.cend(), std::back_inserter(queue)); + }; - { - auto dst = wavBuf; - auto src = reinterpret_cast<uint16_t *>(wavOutput.next(bsize)); - for (auto i = 0u; i < bsize; ++i) - *dst++ = *src++ / 16 + 2048; - m_device->siggen_upload(wavBuf, bsize); - } + std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock); - m_device->siggen_start(); + while (device && device->is_running()) { + const auto next = std::chrono::high_resolution_clock::now() + bufferTime; - while (genRunning) { - auto dst = wavBuf; - auto src = reinterpret_cast<uint16_t *>(wavOutput.next(bsize)); - for (auto i = 0u; i < bsize; ++i) - *dst++ = *src++ / 16 + 2048; - m_device->siggen_upload(wavBuf, bsize); + if (lockDevice.try_lock_until(next)) { + std::vector<stmdsp::dacsample_t> chunk, chunk2; - std::this_thread::sleep_for(std::chrono::milliseconds(delay)); - } + chunk = tryReceiveChunk(device, + std::mem_fn(&stmdsp::device::continuous_read)); + if (drawSamplesInput) { + chunk2 = tryReceiveChunk(device, + std::mem_fn(&stmdsp::device::continuous_read_input)); + } - delete[] wavBuf; -} + lockDevice.unlock(); -static void deviceConnect(); -static void deviceStart(); -static void deviceAlgorithmUpload(); -static void deviceAlgorithmUnload(); -static void deviceGenLoadList(std::string_view list); -static void deviceGenLoadFormula(std::string_view list); + addToQueue(drawSamplesQueue, chunk); + if (drawSamplesInput) + addToQueue(drawSamplesInputQueue, chunk2); -void deviceRenderWidgets() -{ - static char *siggenBuffer = nullptr; - static int siggenOption = 0; - - if (popupRequestSiggen) { - siggenBuffer = new char[65536]; - *siggenBuffer = '\0'; - ImGui::OpenPopup("siggen"); - popupRequestSiggen = false; - } else if (popupRequestBuffer) { - ImGui::OpenPopup("buffer"); - popupRequestBuffer = false; - } else if (popupRequestLog) { - ImGuiFileDialog::Instance()->OpenModal( - "ChooseFileLogGen", "Choose File", ".csv", "."); - popupRequestLog = false; - } - - if (ImGui::BeginPopup("siggen")) { - if (ImGui::RadioButton("List", &siggenOption, 0)) - siggenBuffer[0] = '\0'; - ImGui::SameLine(); - if (ImGui::RadioButton("Formula", &siggenOption, 1)) - siggenBuffer[0] = '\0'; - ImGui::SameLine(); - if (ImGui::RadioButton("Audio File", &siggenOption, 2)) - siggenBuffer[0] = '\0'; - - switch (siggenOption) { - case 0: - ImGui::Text("Enter a list of numbers:"); - ImGui::PushStyleColor(ImGuiCol_FrameBg, {.8, .8, .8, 1}); - ImGui::InputText("", siggenBuffer, 65536); - ImGui::PopStyleColor(); - break; - case 1: - ImGui::Text("Enter a formula. f(x) = "); - ImGui::PushStyleColor(ImGuiCol_FrameBg, {.8, .8, .8, 1}); - ImGui::InputText("", siggenBuffer, 65536); - ImGui::PopStyleColor(); - break; - case 2: - if (ImGui::Button("Choose File")) { - // This dialog will override the siggen popup, closing it. - ImGuiFileDialog::Instance()->OpenModal( - "ChooseFileLogGen", "Choose File", ".wav", "."); + if (logSamplesFile.is_open()) { + for (const auto& s : chunk) + logSamplesFile << s << '\n'; } - break; + } else { + // Device must be busy, back off for a bit. + std::this_thread::sleep_for(std::chrono::milliseconds(50)); } - if (ImGui::Button("Cancel")) { - delete[] siggenBuffer; - ImGui::CloseCurrentPopup(); - } + std::this_thread::sleep_until(next); + } +} - if (ImGui::Button("Save")) { - switch (siggenOption) { - case 0: - deviceGenLoadList(siggenBuffer); - break; - case 1: - deviceGenLoadFormula(siggenBuffer); - break; - case 2: - break; - } +static void feedSigGenTask(std::shared_ptr<stmdsp::device> device) +{ + if (!device) + return; - delete[] siggenBuffer; - ImGui::CloseCurrentPopup(); - } + const auto delay = getBufferPeriod(device); + const auto uploadDelay = getBufferPeriod(device, 0.001); - ImGui::EndPopup(); - } + std::vector<stmdsp::dacsample_t> wavBuf (device->get_buffer_size() * 2, 2048); - if (ImGui::BeginPopup("buffer")) { - static char bufferSizeStr[5] = "4096"; - ImGui::Text("Please enter a new sample buffer size (100-4096):"); - ImGui::PushStyleColor(ImGuiCol_FrameBg, {.8, .8, .8, 1}); - ImGui::InputText("", bufferSizeStr, sizeof(bufferSizeStr), ImGuiInputTextFlags_CharsDecimal); - ImGui::PopStyleColor(); - if (ImGui::Button("Save")) { - if (m_device != nullptr) { - int n = std::clamp(std::stoi(bufferSizeStr), 100, 4096); - m_device->continuous_set_buffer_size(n); - } - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if (ImGui::Button("Cancel")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); + { + std::scoped_lock lock (mutexDeviceLoad); + device->siggen_upload(wavBuf.data(), wavBuf.size()); + device->siggen_start(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - if (ImGuiFileDialog::Instance()->Display("ChooseFileLogGen")) { - if (ImGuiFileDialog::Instance()->IsOk()) { - auto filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); - auto ext = filePathName.substr(filePathName.size() - 4); - - if (ext.compare(".wav") == 0) { - wavOutput = wav::clip(filePathName.c_str()); - if (wavOutput.valid()) - log("Audio file loaded."); - else - log("Error: Bad WAV audio file."); - - delete[] siggenBuffer; - } else if (ext.compare(".csv") == 0) { - logSamplesFile = std::ofstream(filePathName); - if (logSamplesFile.good()) - log("Log file ready."); - } + wavBuf.resize(wavBuf.size() / 2); + std::vector<int16_t> wavIntBuf (wavBuf.size()); + + while (device->is_siggening()) { + const auto next = std::chrono::high_resolution_clock::now() + delay; - ImGuiFileDialog::Instance()->Close(); + wavOutput.next(wavIntBuf.data(), wavIntBuf.size()); + std::transform(wavIntBuf.cbegin(), wavIntBuf.cend(), + wavBuf.begin(), + [](auto i) { return static_cast<stmdsp::dacsample_t>(i / 16 + 2048); }); + + { + std::scoped_lock lock (mutexDeviceLoad); + while (!device->siggen_upload(wavBuf.data(), wavBuf.size())) + std::this_thread::sleep_for(uploadDelay); } + + std::this_thread::sleep_until(next); } } -void deviceRenderDraw() +static void statusTask(std::shared_ptr<stmdsp::device> device) { - if (popupRequestDraw) { - ImGui::Begin("draw", &popupRequestDraw); - ImGui::Checkbox("Draw input", &drawSamplesInput); - { - std::scoped_lock lock (mutexDrawSamples); - auto drawList = ImGui::GetWindowDrawList(); - const ImVec2 p0 = ImGui::GetWindowPos(); - const auto size = ImGui::GetWindowSize(); - //ImVec2 p1 (p0.x + size.x, p0.y + size.y); - //ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255)); - //ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255)); - //drawList->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a); - - const unsigned int didx = 1.f / (size.x / static_cast<float>(drawSamplesBuf.size())); - ImVec2 pp = p0; - for (auto i = 0u; i < drawSamplesBuf.size(); i += didx) { - ImVec2 next (pp.x + 1, p0.y + (float)drawSamplesBuf[i] / 4095.f * size.y); - drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(128, 0, 0, 255))); - pp = next; - } + if (!device) + return; - if (drawSamplesInput) { - pp = p0; - for (auto i = 0u; i < drawSamplesBuf2.size(); i += didx) { - ImVec2 next (pp.x + 1, p0.y + (float)drawSamplesBuf2[i] / 4095.f * size.y); - drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(0, 0, 128, 255))); - pp = next; - } - } + while (device->connected()) { + mutexDeviceLoad.lock(); + const auto [status, error] = device->get_status(); + mutexDeviceLoad.unlock(); + + if (error != stmdsp::Error::None) { + switch (error) { + case stmdsp::Error::NotIdle: + log("Error: Device already running..."); + break; + case stmdsp::Error::ConversionAborted: + log("Error: Algorithm unloaded, a fault occurred!"); + break; + case stmdsp::Error::GUIDisconnect: + // Do GUI events for disconnect if device was lost. + deviceConnect(); + deviceRenderDisconnect(); + return; + break; + default: + log("Error: Device had an issue..."); + break; } - ImGui::End(); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); } } -void deviceRenderMenu() +void deviceLoadAudioFile(const std::string& file) { - if (ImGui::BeginMenu("Run")) { - bool isConnected = m_device != nullptr; - bool isRunning = isConnected && m_device->is_running(); - - static const char *connectLabel = "Connect"; - if (ImGui::MenuItem(connectLabel)) { - deviceConnect(); - connectLabel = isConnected ? "Disconnect" : "Connect"; - } + wavOutput = wav::clip(file); + if (wavOutput.valid()) + log("Audio file loaded."); + else + log("Error: Bad WAV audio file."); +} - ImGui::Separator(); - static const char *startLabel = "Start"; - if (ImGui::MenuItem(startLabel, nullptr, false, isConnected)) { - deviceStart(); - startLabel = isRunning ? "Stop" : "Start"; - } +void deviceLoadLogFile(const std::string& file) +{ + logSamplesFile = std::ofstream(file); + if (logSamplesFile.is_open()) + log("Log file ready."); + else + log("Error: Could not open log file."); +} -/** -TODO test siggen formula -TODO improve siggen audio streaming -TODO draw: smoothly chain captures - */ - if (ImGui::MenuItem("Upload algorithm", nullptr, false, isConnected)) - deviceAlgorithmUpload(); - if (ImGui::MenuItem("Unload algorithm", nullptr, false, isConnected)) - deviceAlgorithmUnload(); - ImGui::Separator(); - if (ImGui::Checkbox("Measure Code Time", &measureCodeTime)) { - if (!isConnected) - measureCodeTime = false; - } - if (ImGui::Checkbox("Draw samples", &drawSamples)) { - if (isConnected) { - if (drawSamples) - popupRequestDraw = true; - } else { - drawSamples = false; - } - } - if (ImGui::Checkbox("Log results...", &logResults)) { - if (isConnected) { - if (logResults) - popupRequestLog = true; - else if (logSamplesFile.is_open()) - logSamplesFile.close(); +bool deviceGenStartToggle() +{ + if (m_device) { + const bool running = m_device->is_siggening(); + + if (!running) { + if (wavOutput.valid()) { + std::thread(feedSigGenTask, m_device).detach(); } else { - logResults = false; + std::scoped_lock dlock (mutexDeviceLoad); + m_device->siggen_start(); } - } - if (ImGui::MenuItem("Set buffer size...", nullptr, false, isConnected)) { - popupRequestBuffer = true; - } - ImGui::Separator(); - if (ImGui::MenuItem("Load signal generator", nullptr, false, isConnected)) { - popupRequestSiggen = true; - } - static const char *startSiggenLabel = "Start signal generator"; - if (ImGui::MenuItem(startSiggenLabel, nullptr, false, isConnected)) { - if (m_device != nullptr) { - if (!genRunning) { - genRunning = true; - if (wavOutput.valid()) - std::thread(feedSigGenTask, m_device).detach(); - else - m_device->siggen_start(); - log("Generator started."); - startSiggenLabel = "Stop signal generator"; - } else { - genRunning = false; - m_device->siggen_stop(); - log("Generator stopped."); - startSiggenLabel = "Start signal generator"; - } + log("Generator started."); + } else { + { + std::scoped_lock dlock (mutexDeviceLoad); + m_device->siggen_stop(); } + log("Generator stopped."); } - ImGui::EndMenu(); + return !running; } + + return false; } -void deviceRenderToolbar() +void deviceUpdateDrawBufferSize(double timeframe) { - ImGui::SameLine(); - if (ImGui::Button("Upload")) - deviceAlgorithmUpload(); - ImGui::SameLine(); - ImGui::SetNextItemWidth(100); - if (ImGui::BeginCombo("", sampleRatePreview)) { - for (int i = 0; i < 6; ++i) { - if (ImGui::Selectable(sampleRateList[i])) { - sampleRatePreview = sampleRateList[i]; - if (m_device != nullptr && !m_device->is_running()) - m_device->set_sample_rate(i); - } - } - ImGui::EndCombo(); - } + drawSamplesBufferSize = std::round( + m_device->get_sample_rate() * timeframe); } -void deviceConnect() +void deviceSetSampleRate(unsigned int rate) { - if (m_device == nullptr) { + do { + m_device->set_sample_rate(rate); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } while (m_device->get_sample_rate() != rate); +} + +bool deviceConnect() +{ + static std::thread statusThread; + + if (!m_device) { stmdsp::scanner scanner; - if (auto devices = scanner.scan(); devices.size() > 0) { - m_device = new stmdsp::device(devices.front()); - if (m_device->connected()) { - sampleRatePreview = sampleRateList[m_device->get_sample_rate()]; - log("Connected!"); - } else { - delete m_device; - m_device = nullptr; - log("Failed to connect."); + if (const auto devices = scanner.scan(); !devices.empty()) { + try { + m_device.reset(new stmdsp::device(devices.front())); + } catch (...) { + log("Failed to connect (check permissions?)."); + m_device.reset(); + } + + if (m_device) { + if (m_device->connected()) { + log("Connected!"); + statusThread = std::thread(statusTask, m_device); + statusThread.detach(); + return true; + } else { + m_device.reset(); + log("Failed to connect."); + } } } else { log("No devices found."); } } else { - delete m_device; - m_device = nullptr; + m_device->disconnect(); + if (statusThread.joinable()) + statusThread.join(); + m_device.reset(); log("Disconnected."); } + + return false; } -void deviceStart() +void deviceStart(bool logResults, bool drawSamples) { - if (m_device == nullptr) { + if (!m_device) { log("No device connected."); return; } if (m_device->is_running()) { - m_device->continuous_stop(); - if (logResults) { + { + std::scoped_lock lock (mutexDrawSamples, mutexDeviceLoad); + std::this_thread::sleep_for(std::chrono::microseconds(150)); + m_device->continuous_stop(); + } + if (logSamplesFile.is_open()) { logSamplesFile.close(); - logResults = false; log("Log file saved and closed."); } log("Ready."); } else { - if (measureCodeTime) { - m_device->continuous_start_measure(); - std::thread(measureCodeTask, m_device).detach(); - } else { - m_device->continuous_start(); - if (drawSamples || logResults || wavOutput.valid()) - std::thread(drawSamplesTask, m_device).detach(); - } + m_device->continuous_start(); + if (drawSamples || logResults || wavOutput.valid()) + std::thread(drawSamplesTask, m_device).detach(); + log("Running."); } } -void deviceAlgorithmUpload() +void deviceStartMeasurement() { - if (m_device == nullptr) { - log("No device connected."); - return; + if (m_device && m_device->is_running()) { + m_device->measurement_start(); + std::thread(measureCodeTask, m_device).detach(); } +} - if (m_device->is_running()) - return; - - if (std::ifstream algo (tempFileName + ".o"); algo.good()) { +void deviceAlgorithmUpload() +{ + if (!m_device) { + log("No device connected."); + } else if (m_device->is_running()) { + log("Cannot upload algorithm while running."); + } else if (auto algo = compileOpenBinaryFile(); algo.is_open()) { std::ostringstream sstr; sstr << algo.rdbuf(); auto str = sstr.str(); @@ -473,60 +371,111 @@ void deviceAlgorithmUpload() void deviceAlgorithmUnload() { - if (m_device == nullptr) { + if (!m_device) { log("No device connected."); - return; - } - - if (!m_device->is_running()) { + } else if (m_device->is_running()) { + log("Cannot unload algorithm while running."); + } else { 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()) { + const auto itend = std::find_if(it, list.cend(), + [](char c) { return !isdigit(c); }); 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); - if (ec != std::errc()) + const auto ec = std::from_chars(it, itend, n).ec; + if (ec != std::errc()) { + log("Error: Bad data in sample list."); break; - - samples.push_back(n & 4095); - if (end == listStr.end()) + } else if (n > 4095) { + log("Error: Sample data value larger than max of 4095."); break; - listStr = listStr.substr(numberEnd + 1); + } else { + samples.push_back(n & 4095); + if (samples.size() >= stmdsp::SAMPLES_MAX * 2) { + log("Error: Too many samples for signal generator."); + break; + } + } + + it = std::find_if(itend, list.cend(), isdigit); } - if (samples.size() <= stmdsp::SAMPLES_MAX * 2) { + if (it == list.cend()) { // 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 != nullptr) - 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."); } } -void deviceGenLoadFormula(std::string_view formula) +void deviceGenLoadFormula(const std::string& formula) { auto samples = deviceGenLoadFormulaEval(formula); - if (samples.size() > 0) { - if (m_device != nullptr) - m_device->siggen_upload(&samples[0], samples.size()); - + if (!samples.empty()) { + m_device->siggen_upload(samples.data(), samples.size()); log("Generator ready."); } else { log("Error: Bad formula."); } } +std::size_t pullFromQueue( + std::deque<stmdsp::dacsample_t>& queue, + CircularBuffer<std::vector, stmdsp::dacsample_t>& circ) +{ + // We know how big the circular buffer should be to hold enough samples to + // fill the current draw samples view. + // If the given buffer does not match this size, notify the caller. + // TODO this could be done better... drawSamplesBufferSize should be a GUI- + // only thing. + if (circ.size() != drawSamplesBufferSize) + return drawSamplesBufferSize; + + std::scoped_lock lock (mutexDrawSamples); + + // The render code will draw all of the new samples we add to the buffer. + // So, we must provide a certain amount of samples at a time to make the + // render appear smooth. + // The 1.025 factor keeps us on top of the stream; don't want to fall + // behind. + const double FPS = ImGui::GetIO().Framerate; + const auto desiredCount = m_device->get_sample_rate() / FPS; + + // Transfer from the queue to the render buffer. + auto count = std::min(queue.size(), static_cast<std::size_t>(desiredCount)); + while (count--) { + circ.put(queue.front()); + queue.pop_front(); + } + + return 0; +} + +/** + * Pulls a render frame's worth of samples from the draw samples queue, adding + * the samples to the given buffer. + */ +std::size_t pullFromDrawQueue( + CircularBuffer<std::vector, stmdsp::dacsample_t>& circ) +{ + return pullFromQueue(drawSamplesQueue, circ); +} + +std::size_t pullFromInputDrawQueue( + CircularBuffer<std::vector, stmdsp::dacsample_t>& circ) +{ + return pullFromQueue(drawSamplesInputQueue, circ); +} + |